【问题标题】:Automatically Sizing UIView after Adding to Window添加到窗口后自动调整 UIView 的大小
【发布时间】:2011-02-09 04:55:48
【问题描述】:

注意:这可能与Subview Doesnt AutoSize When Added to Root View Controller 重复


我有一个 iPad 应用程序,可以在其主窗口的不同视图之间切换。视图切换代码如下所示:

- (void)switchToViewController:(UIViewController*)viewController {
    if (currentViewController != viewController) {
        [currentViewController.view removeFromSuperview];
        currentViewController = viewController;
        [window addSubview:viewController.view];
    }
}

问题是当新视图(UISplitView)以横向显示时,它的大小无法填满整个窗口。右边有一大片空白的黑色空间。看起来视图只有 768 像素宽,而不是横向窗口的 1024 像素宽。

如果我将设备旋转为纵向,然后再旋转回横向,则视图本身的大小会正确。

如果设备处于纵向,则一切正常。如果 UISplitView 是我显示的第一个视图,它的大小也会正确。仅当我在以横向显示另一个视图后切换到它时才会出现此问题。

那么,有没有办法强制 iPhone OS 在视图添加到窗口后调整其大小?

我试过打电话给sizeToFitsetNeedsLayout。我还尝试将视图的bounds 设置为窗口的bounds,并尝试将frame 设置为与前一个视图的框架相匹配。

【问题讨论】:

    标签: iphone uiview ipad uiwindow


    【解决方案1】:

    这绝对有可能! :-)

    你可以在这里查看我的回购: https://github.com/hfossli/AGWindowView

    它会自动处理任何旋转和帧变化,因此您不必担心。

    如果您想担心这个,那么您可以剪切并粘贴最重要的部分

    # 1 向窗口添加视图

    [[UIApplication sharedApplication] keyWindow] addSubview:aView];
    

    # 2 添加监听器并更新视图

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
    

    记得删除通知监听

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    # 3 做数学题

    - (void)statusBarFrameOrOrientationChanged:(NSNotification *)notification
    {
        /*
         This notification is most likely triggered inside an animation block,
         therefore no animation is needed to perform this nice transition.
         */
        [self rotateAccordingToStatusBarOrientationAndSupportedOrientations];
    }
    
    - (void)rotateAccordingToStatusBarOrientationAndSupportedOrientations
    {
        UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
        CGFloat angle = UIInterfaceOrientationAngleOfOrientation(statusBarOrientation);
        CGFloat statusBarHeight = [[self class] getStatusBarHeight];
    
        CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
        CGRect frame = [[self class] rectInWindowBounds:self.window.bounds statusBarOrientation:statusBarOrientation statusBarHeight:statusBarHeight];
    
        [self setIfNotEqualTransform:transform frame:frame];
    }
    
    - (void)setIfNotEqualTransform:(CGAffineTransform)transform frame:(CGRect)frame
    {
        if(!CGAffineTransformEqualToTransform(self.transform, transform))
        {
            self.transform = transform;
        }
        if(!CGRectEqualToRect(self.frame, frame))
        {
            self.frame = frame;
        }
    }
    
    + (CGFloat)getStatusBarHeight
    {
        UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
        if(UIInterfaceOrientationIsLandscape(orientation))
        {
            return [UIApplication sharedApplication].statusBarFrame.size.width;
        }
        else
        {
            return [UIApplication sharedApplication].statusBarFrame.size.height;
        }
    }
    
    + (CGRect)rectInWindowBounds:(CGRect)windowBounds statusBarOrientation:(UIInterfaceOrientation)statusBarOrientation statusBarHeight:(CGFloat)statusBarHeight
    {    
        CGRect frame = windowBounds;
        frame.origin.x += statusBarOrientation == UIInterfaceOrientationLandscapeLeft ? statusBarHeight : 0;
        frame.origin.y += statusBarOrientation == UIInterfaceOrientationPortrait ? statusBarHeight : 0;
        frame.size.width -= UIInterfaceOrientationIsLandscape(statusBarOrientation) ? statusBarHeight : 0;
        frame.size.height -= UIInterfaceOrientationIsPortrait(statusBarOrientation) ? statusBarHeight : 0;
        return frame;
    }
    
    CGFloat UIInterfaceOrientationAngleOfOrientation(UIInterfaceOrientation orientation)
    {
        CGFloat angle;
    
        switch (orientation)
        {
            case UIInterfaceOrientationPortraitUpsideDown:
                angle = M_PI;
                break;
            case UIInterfaceOrientationLandscapeLeft:
                angle = -M_PI_2;
                break;
            case UIInterfaceOrientationLandscapeRight:
                angle = M_PI_2;
                break;
            default:
                angle = 0.0;
                break;
        }
    
        return angle;
    }
    
    UIInterfaceOrientationMask UIInterfaceOrientationMaskFromOrientation(UIInterfaceOrientation orientation)
    {
        return 1 << orientation;
    }
    

    祝你好运!

    【讨论】:

      【解决方案2】:

      这行得通,但它似乎有点hacky:

      - (void)switchToViewController:(UIViewController *)viewController {
          if (viewController != currentViewController) {
              UIInterfaceOrientation orientation = currentViewController.interfaceOrientation;
              [currentViewController.view removeFromSuperview];
      
              currentViewController = viewController;
              UIView *view = viewController.view;
      
              // Set appropriate view frame (it won't be autosized by addSubview:)
              CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
              if (UIInterfaceOrientationIsLandscape(orientation)) {
                  // Need to flip the X-Y coordinates for landscape
                  view.frame = CGRectMake(appFrame.origin.y, appFrame.origin.x, appFrame.size.height, appFrame.size.width);
              }
              else {
                  view.frame = appFrame;
              }
      
              [window addSubview:view];
          }
      }
      

      【讨论】:

      • 你不会在这里遇到问题,因为你没有计算导航栏的20px吗?
      【解决方案3】:

      该窗口可能包含除您的视图之外的其他 UI 元素。您的示例中的 20 像素差异是状态栏的高度。

      [[UIApplication sharedApplication] statusBarFrame].height;
      

      窗口和屏幕都不旋转。仅当您切换了高度和宽度时,才能获取它们的框架并将它们用于旋​​转视图。

      如果您使用的是 UIViewController,请尝试从此方法返回 YES:

      - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait
      

      【讨论】:

      • 事物不只是 20 像素;它们相差 320 像素。我从 shouldAutorotateToInterfaceOrientation: 返回 YES,一切都以正确的方向显示。只是视图大小不对。
      • 我所说的 20 像素是 1004 vs 1024 和 748 vs 768。通过将值 20 硬编码到宽度或高度中,您为状态栏留出了空间。
      【解决方案4】:

      我遇到了同样的问题,但我用这行代码修复了它:

      - (void)changeRow:(NSNotification *)notification {
      [window addSubview:new.view];
      [old.view removeFromSuperview];
      [new.view removeFromSuperview];
      [window addSubview:new.view];
      

      }

      您必须添加新视图,然后删除旧视图和新视图,然后添加新视图。我不知道为什么,但这确实有效。

      【讨论】:

        【解决方案5】:

        Fossli 的回答对于 iPad 来说是正确的。但是,我有一个需要支持的通用应用程序。因此需要进行一些调整。

        将以下内容添加到 AppDelegate.h

        @property (strong, nonatomic) UIImageView *imageView;
        

        将以下内容添加到 AppDelegate.m

        @synthesize imageView;
        
        - (void)orientationChanged:(NSNotification *)notification
        {
            UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;   
            if (! (UIInterfaceOrientationIsLandscape(deviceOrientation) ||
                   UIInterfaceOrientationIsPortrait(deviceOrientation)))
            {
                // May be "UIInterfaceOrientationUnknown" which does not appear to be a defined value anywhere.
                return;
            }
        
            [imageView setImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
        
            /*
             iOS Image Sizes
        
             iPhone/iPod    Portrait 320 x 480 (640 x 960 @2x)
             iPad           Portrait 768 x 1004 (1536 x 2008 @2x)
                            Landscape 1024 x 748 (2048 x 1496 @2x)
        
             iPad window bounds in both orientations 768 x 1024  (needs manual swap in landscape)
             iPhone window bounds in both orientations 320 x 480 (needs manual swap in landscape)
        
             Note the size variations between the required default launch image sizes and
             the size of the window bounds.
             iPhone/iPod only requires rotations.
             iPad needs origin or size adjustments depending on orientation.
             */
        
            CGFloat angle = 0.0;
            CGRect newFrame = [[self window] bounds];
        
            // How to get size of status bar
            // Size of status bar gets all wonky on rotations so just set it manually
            // CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
            CGSize statusBarSize = CGSizeMake(20.0, 20.0);
        
            if (deviceOrientation == UIInterfaceOrientationPortraitUpsideDown)
            {
                angle = M_PI; 
                if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
                {
                    newFrame.size.height -= statusBarSize.height;
                }
            }
            else if (deviceOrientation == UIInterfaceOrientationLandscapeLeft)
            {
                angle = - M_PI / 2.0f;        
                if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
                {
                    newFrame.origin.x += statusBarSize.height;
                    newFrame.size.width += statusBarSize.height;
                }
            }
            else if (deviceOrientation == UIInterfaceOrientationLandscapeRight)
            {
                angle = M_PI / 2.0f;
                if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
                {
                    newFrame.size.width -= statusBarSize.height;
                }
            }
            else
            {
                angle = 0.0;
                if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
                {
                    newFrame.origin.y += statusBarSize.height;
                    newFrame.size.height -= statusBarSize.height;
                }
            } 
        
            imageView.transform = CGAffineTransformMakeRotation(angle);
            imageView.frame = newFrame;
        }
        
        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
        {   
            // Add background image to window with orientation changes so that it is visible in all views.
            // A listener is added since subviews do not receive orientation changes.
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object: nil];
        
            UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;      
            imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
            [[self window] addSubview:imageView];
        
            return YES;
        }
        

        将以下内容添加到 Utility.h

        + (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation;
        

        将以下内容添加到 Utility.m

        + (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation
        {
            NSString *imageName = nil;
        
            if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
            {
                if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
                {
                    imageName = @"Default-Landscape~ipad.png";
                }
                else
                {
                    imageName = @"Default-Portrait~ipad.png";
                }
            }
            else
            {
                if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
                {
                    imageName = @"Default-Landscape~iphone.png";
                }
                else
                {
                    imageName = @"Default.png";
                }
            }
        
            return imageName;
        }
        

        【讨论】:

        • 另外请注意,如果您不显示应用的状态栏(您可能应该),您可能不需要使用状态栏高度进行一些调整。
        【解决方案6】:

        iOS7 的窗口与 iOS8/9 的窗口有不同的行为。

        iOS7 的键盘窗口和 iOS8/9 的所有窗口始终具有正确的方向和大小。因此,您可以观察尺寸变化事件并更新您的视图框架。

        但iOS7的其他窗口始终保持纵向和大小。旋转后您需要更新视图的变换。

        您需要像这样观察 UIApplicationWillChangeStatusBarOrientationNotification 并更新 UIView 的大小:

        @interface MyView : UIView
        
        @end
        
        @implementation MyView
        
        - (instancetype)init
        {
            if (self = [super init]) {
                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeOrientationHandler:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
            }
            return self;
        }
        
        - (void)dealloc
        {
            [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
        }
        
        - (void)updateTransformWithOrientation:(UIInterfaceOrientation)orientation
        {
            CGFloat width = CGRectGetWidth(self.window.bounds);
            CGFloat height = CGRectGetHeight(self.window.bounds);
            if (width > height) {
                CGFloat temp = width;
                width = height;
                height = temp;
            }
            CGFloat offset = (height - width) / 2;
            CGAffineTransform transform;
            switch (orientation) {
                case UIInterfaceOrientationLandscapeLeft:
                    transform = CGAffineTransformMakeTranslation(-offset, offset);
                    transform = CGAffineTransformRotate(transform, -M_PI_2);
                    break;
                case UIInterfaceOrientationLandscapeRight:
                    transform = CGAffineTransformMakeTranslation(-offset, offset);
                    transform = CGAffineTransformRotate(transform, M_PI_2);
                    break;
                case UIInterfaceOrientationPortraitUpsideDown:
                    transform = CGAffineTransformMakeRotation(-M_PI);
                    break;
                default:
                    transform = CGAffineTransformIdentity;
                    break;
            }
            self.transform = transform;
            self.frame = CGRectMake(0, 0, width, height);
        }
        
        - (void)updateFrameWithOrientation:(UIInterfaceOrientation)orientation
        {
            CGFloat width = CGRectGetWidth(self.window.bounds);
            CGFloat height = CGRectGetHeight(self.window.bounds);
            if (width > height) {
                CGFloat temp = width;
                width = height;
                height = temp;
            }
            switch (orientation) {
                case UIInterfaceOrientationLandscapeLeft:
                case UIInterfaceOrientationLandscapeRight:
                    self.frame = CGRectMake(0, 0, height, width);
                    break;
                default:
                    self.frame = CGRectMake(0, 0, width, height);
                    break;
            }
        }
        
        - (void)updateWithOrientation:(UIInterfaceOrientation)orientation
        {
            BOOL isIos7 = [[UIDevice currentDevice].systemVersion floatValue] < 8.0;
            BOOL isKeyboardWindow = [self.window isKindOfClass:NSClassFromString(@"UITextEffectsWindow")];
            if (isIos7 == YES && isKeyboardWindow == NO) {
                [self updateTransformWithOrientation:orientation];
            } else {
                [self updateFrameWithOrientation:orientation];
            }
        }
        
        - (void)changeOrientationHandler:(NSNotification *)notification
        {
            [UIView animateWithDuration:0.25 animations:^{
                UIInterfaceOrientation orientation = (UIInterfaceOrientation)[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
                [self updateWithOrientation:orientation];
            }];
        }
        
        @end
        

        【讨论】:

          猜你喜欢
          • 2017-03-12
          • 1970-01-01
          • 1970-01-01
          • 2013-07-16
          • 1970-01-01
          • 2011-12-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多