Автоматическое изменение размера UIView после добавления в окно
Примечание: это может быть дубликат Subview не автоматически изменяется при добавлении в корневой контроллер вида
У меня есть приложение для iPad, которое переключается между различными видами в главном окне. Код переключения вида выглядит следующим образом:
- (void)switchToViewController:(UIViewController*)viewController {
if (currentViewController != viewController) {
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
[window addSubview:viewController.view];
}
}
Проблема заключается в том, что когда новый вид (UISplitView) появляется в альбомной ориентации, он не заполняет все окно. Справа-большое пустое черное пространство. Похоже, что ширина изображения составляет всего 768 пикселей, вместо ширины ландшафтного окна в 1024 пикселя.
Если я поворачиваю устройство в книжную, а затем обратно в альбомную ориентацию, вид сам себя правильно определяет.
Если устройство находится в портретной ориентации, все работает нормально. UISplitView также получает правильный размер, если это первый вид, который я показываю. Проблема возникает только в том случае, если я переключаюсь на него после того, как был показан другой вид, в ландшафте.
Итак, есть ли способ заставить iPhone OS изменить размер представления после того, как оно было добавлено к окну?
Я пробовал звонить sizeToFit
и setNeedsLayout
. Я также попытался установить вид bounds
в окно bounds
, и я попытался установить frame
, чтобы соответствовать рамке предыдущего вида.
6 ответов:
Это абсолютно возможно! :-)
Вы можете проверить мое РЕПО здесь: 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; }
Удачи!
Это работает, но кажется немного банальным:
- (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]; } }
Окно может содержать и другие элементы пользовательского интерфейса, кроме вашего представления. Разница в 20 пикселей в вашем примере-это высота строки состояния.
[[UIApplication sharedApplication] statusBarFrame].height;
Ни окно, ни экран не вращаются. Получение их кадров и использование их для повернутого вида будет работать только в том случае, если вы переключили высоту и ширину.
Если вы используете UIViewController, попробуйте вернуть YES из этого метода:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait
У меня была та же проблема, но я исправил ее с помощью следующих строк кода:
- (void)changeRow:(NSNotification *)notification { [window addSubview:new.view]; [old.view removeFromSuperview]; [new.view removeFromSuperview]; [window addSubview:new.view];
}
Необходимо добавить новое представление, затем удалить старое и новое, а затем добавить новое представление. Не знаю почему, но это работает.
Ответ Фоссли верен для 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; }
Добавьте к утилите следующее.h
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation;
Добавьте к утилите следующее.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; }
Окна 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