原來項目中用到一個很好用的自定義對話框 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;
}
});
}
];
至此,大功告成!