Automatically Sizing UIView after Adding to Window

前端 未结 6 903
孤街浪徒
孤街浪徒 2020-12-16 06:41

Note: This may be a duplicate of Subview Doesnt AutoSize When Added to Root View Controller


I have an iPad app that switches between different views in its mai

相关标签:
6条回答
  • 2020-12-16 06:56

    I got the same problem, but i fixed it with this lines of code:

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

    }

    You must add the new view, then remove the old and the new and then add the new view. I don't know why, but that works.

    0 讨论(0)
  • 2020-12-16 06:59

    This is absolutely possible! :-)

    You can check out my repo here: https://github.com/hfossli/AGWindowView

    It will automatically deal with any rotation and framechanges so you won't have to worry about that.

    If you like to worry about that then you can just cut and paste the most important parts

    # 1 Add view to window

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

    # 2 Add listener and update view

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

    Remember to remove notification listening

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    

    # 3 Do the math

    - (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;
    }
    

    Good luck!

    0 讨论(0)
  • 2020-12-16 06:59

    The window may include other UI elements besides your view. The 20 pixel difference in your example is the height of the status bar.

    [[UIApplication sharedApplication] statusBarFrame].height;
    

    Neither the window nor screen rotate. Getting their frames and using them for a rotated view will only work if you have switched the height and width.

    If you are using a UIViewController, try returning YES from this method:

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait
    
    0 讨论(0)
  • 2020-12-16 07:01

    Fossli's answer is correct for iPad. However, I have a universal app that I needed to support. Therefore some adjustments are necessary.

    Add the following to AppDelegate.h

    @property (strong, nonatomic) UIImageView *imageView;
    

    Add the following to 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;
    }
    

    Add the following to Utility.h

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

    Add the following to 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;
    }
    
    0 讨论(0)
  • 2020-12-16 07:03

    This works, but it seems a little 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];
        }
    }
    
    0 讨论(0)
  • 2020-12-16 07:05

    Windows of iOS7 have different behaviors with windows of iOS8/9.

    Keyboard window of iOS7 and all windows of iOS8/9 always have correct orientation & size. So you can observer the size change events and update the frame of your view.

    But other windows of iOS7 always keep the portrait orientation and size. You need update transform of your view after rotation.

    You need to observer UIApplicationWillChangeStatusBarOrientationNotification and update size of your UIView like this:

    @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
    
    0 讨论(0)
提交回复
热议问题