原来项目中用到一个很好用的自定义对话框 CustomIOS7AlertVIew
这个对话框在iOS 7下完全没有问题。
self.alertView = [[CustomIOS7AlertView alloc] init];
self.alertView.delegate = self;
self.dvController = [[ZYAlertDirectionalVersionViewController alloc] initWithNibName:@"ZYAlertDirectionalVersion" bundle:[NSBundle mainBundle]];
self.dvController.delegate = self;
self.dvController.view.layer.cornerRadius = 8;
[self.alertView setButtonTitles:[NSMutableArray arrayWithObjects: nil]];
[self.alertView setContainerView:self.dvController.view];
[self.alertView show];
使用方法很简单。视图是在xib里面的,使用setContainerView放入CustomIOS7AlertView里面就可以了。横竖屏也可以适应。
但是在iOS 8中,横屏的时候,弹出的框确实竖屏的样子。
查了资料后,才知道,原来是iOS 8 中UIScreen的方向与实际方向同步了,而不是iOS7中一直竖屏。所以iOS7中做了交换width、height的动作,而iOS 8中就多余了。
摘录一段:
Yes, it's orientation-dependent in iOS8, not a bug. You could review session 214 from WWDC 2014 for more info: "View Controller Advancements in iOS 8"
Quote from the presentation:
UIScreen is now interface oriented:
- [UIScreen bounds] now interface-oriented
- [UIScreen applicationFrame] now interface-oriented
- Status bar frame notifications are interface-oriented
- Keyboard frame notifications are interface-oriented
CustomIOS7AlertView中有四个地方需要修改:
1. 创建containerView的时候,计算屏幕大小。 8.0以上不需要交换高宽。
// Helper function: count and return the screen's size
- (CGSize)countScreenSize
{
if (buttonTitles!=NULL && [buttonTitles count] > 0) {
buttonHeight = kCustomIOS7AlertViewDefaultButtonHeight;
buttonSpacerHeight = kCustomIOS7AlertViewDefaultButtonSpacerHeight;
} else {
buttonHeight = 0;
buttonSpacerHeight = 0;
}
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
//2015-1-27 dinghongyan 8.0以上适配。不转,屏幕大小对应屏幕方向。
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
CGFloat tmp = screenWidth;
screenWidth = screenHeight;
screenHeight = tmp;
}
}
return CGSizeMake(screenWidth, screenHeight);
}
2. show的时候,添加到window上,不需要根据方向,在代码中转换。
// Create the dialog view, and animate opening the dialog
- (void)show
{
dialogView = [self createContainerView];
dialogView.layer.shouldRasterize = YES;
dialogView.layer.rasterizationScale = [[UIScreen mainScreen] scale];
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [[UIScreen mainScreen] scale];
#if (defined(__IPHONE_7_0))
if (useMotionEffects) {
[self applyMotionEffects];
}
#endif
dialogView.layer.opacity = 0.5f;
dialogView.layer.transform = CATransform3DMakeScale(1.3f, 1.3f, 1.0);
self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0];
[self addSubview:dialogView];
// Can be attached to a view or to the top most window
// Attached to a view:
if (parentView != NULL) {
[parentView addSubview:self];
// Attached to the top most window (make sure we are using the right orientation):
} else {
//2015-1-27 dinghongyan 8.0以上适配。不转。会自适应当前方向。
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
switch (interfaceOrientation) {
case UIInterfaceOrientationLandscapeLeft:
self.transform = CGAffineTransformMakeRotation(M_PI * 270.0 / 180.0);
break;
case UIInterfaceOrientationLandscapeRight:
self.transform = CGAffineTransformMakeRotation(M_PI * 90.0 / 180.0);
break;
case UIInterfaceOrientationPortraitUpsideDown:
self.transform = CGAffineTransformMakeRotation(M_PI * 180.0 / 180.0);
break;
default:
break;
}
}
[self setFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
[[[[UIApplication sharedApplication] windows] firstObject] addSubview:self];
}
[UIView animateWithDuration:0.2f delay:0.0 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4f];
dialogView.layer.opacity = 1.0f;
dialogView.layer.transform = CATransform3DMakeScale(1, 1, 1);
}
completion:NULL
];
}
3. 键盘弹出事件中,横屏不需要交换高宽
// Handle keyboard show/hide changes
- (void)keyboardWillShow: (NSNotification *)notification
{
CGSize screenSize = [self countScreenSize];
CGSize dialogSize = [self countDialogSize];
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
//2015-1-29 dinghongyan iOS8适配。
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
CGFloat tmp = keyboardSize.height;
keyboardSize.height = keyboardSize.width;
keyboardSize.width = tmp;
}
}
[UIView animateWithDuration:0.2f delay:0.0 options:UIViewAnimationOptionTransitionNone
animations:^{
dialogView.frame = CGRectMake((screenSize.width - dialogSize.width) / 2, (screenSize.height - keyboardSize.height - dialogSize.height) / 2, dialogSize.width, dialogSize.height);
}
completion:nil
];
}
4. 转屏的时候,不要代码中强制转。因为转屏的时候,原来的代码是整个dialogView旋转的,所以底层的View旋转了,上层的containerView和我自定义的view都转了。
实际上,底层的View转不转都无所谓的,不转的话,frame要调整。而上层的containerView和自定义的view是不需要转的。
所以,索性不使用旋转,而是改变底层view的frame。
// Handle device orientation changes
- (void)deviceOrientationDidChange: (NSNotification *)notification
{
// If dialog is attached to the parent view, it probably wants to handle the orientation change itself
if (parentView != NULL) {
return;
}
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
CGAffineTransform rotation = [self changeOrientation];
[UIView animateWithDuration:0.2f delay:0.0 options:UIViewAnimationOptionTransitionNone
animations:^{
dialogView.transform = rotation;
}
completion:^(BOOL finished){
// fix errors caused by being rotated one too many times
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
UIInterfaceOrientation endInterfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (interfaceOrientation != endInterfaceOrientation) {
dialogView.transform = [self changeOrientation];
}
});
}
];
}else{
//2015-1-27 dinghongyan 8.0以上适配。不转,改frame
[UIView animateWithDuration:0.2f delay:0.0 options:UIViewAnimationOptionTransitionNone
animations:^{
self.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
dialogView.center = self.center;
}
completion:^(BOOL finished){
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
// fix errors caused by being rotated one too many times
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
UIInterfaceOrientation endInterfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (interfaceOrientation != endInterfaceOrientation) {
//如果转屏失败,再转回去
self.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
dialogView.center = self.center;
}
});
}
];
}
}
//iOS7中转屏处理
-(CGAffineTransform)changeOrientation{
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
CGFloat startRotation = [[self valueForKeyPath:@"layer.transform.rotation.z"] floatValue];
CGAffineTransform rotation;
switch (interfaceOrientation) {
case UIInterfaceOrientationLandscapeLeft:
rotation = CGAffineTransformMakeRotation(-startRotation + M_PI * 270.0 / 180.0);
break;
case UIInterfaceOrientationLandscapeRight:
rotation = CGAffineTransformMakeRotation(-startRotation + M_PI * 90.0 / 180.0);
break;
case UIInterfaceOrientationPortraitUpsideDown:
rotation = CGAffineTransformMakeRotation(-startRotation + M_PI * 180.0 / 180.0);
break;
default:
rotation = CGAffineTransformMakeRotation(-startRotation + 0.0);
break;
}
return rotation;
}
5. 这个应该是ps了。跟适配没有关系,但是是CustomIOS7AlertView提醒我要注意的一个地方。
就是转屏失败怎么办?
原来的代码,在转屏失败的时候,很萌的来了一句TODO
UIInterfaceOrientation endInterfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (interfaceOrientation != endInterfaceOrientation) {
// TODO user moved phone again before than animation ended: rotation animation can introduce errors here
}
如果按照旋转角度的话,其实我拎不清的。左转右转、多少度神马的,转不过弯儿来。但是改frame的话,就好做多了。只要失败的时候,再根据当前的屏幕重新调整一次就好了。于是:
//2015-1-27 dinghongyan 8.0以上适配。不转,改frame
[UIView animateWithDuration:0.2f delay:0.0 options:UIViewAnimationOptionTransitionNone
animations:^{
self.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
dialogView.center = self.center;
}
completion:^(BOOL finished){
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
// fix errors caused by being rotated one too many times
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
UIInterfaceOrientation endInterfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (interfaceOrientation != endInterfaceOrientation) {
//如果转屏失败,再转回去
self.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
dialogView.center = self.center;
}
});
}
];
至此,大功告成!