天天看點

CustomIOS7AlertView 适配 iOS 8

原來項目中用到一個很好用的自定義對話框 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中,橫屏的時候,彈出的框确實豎屏的樣子。

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;
                                 }
                             });
                         }
         ];
           

至此,大功告成!

CustomIOS7AlertView 适配 iOS 8