iOS 13 适配总结

自从6月份的 WWDC大会展示了iOS 13的新版本之后,广大开发者朋友又面临着新一轮的系统升级适配工作;随着苹果9月份发布会脚步的临近,对公司的App升级适配势在必行。

iOS 13发现问题回顾

  • 禁止用户获取或直接设置私有属性:调用setValue:forKeyPath:valueForKey:方法会引起App崩溃。例如:UITextField修改_placeholderLabel.textColorUISearchBar修改_searchField
  • UITextFieldleftViewrightView调整:部分视图位置显示异常
  • UITabBar部分调整:UITabBarItem播放gif显示比例有问题;UITabBarItem只显示图片时,图片位置偏移;Badge文字显示偏大
  • UITableViewcell选中样式失效
  • 第三方SDK的闪退兼容问题
将所有问题归纳总结,得出以下几点解决方案的建议和示例代码,记录一下

1. UITextField

  • 设置placeholder引起的闪退

    iOS 13之前,设置placeholder有三种方案:

    • 基于KVO简单粗暴的修改私有属性(iOS 13禁用)

      [textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
      [textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];
      
    • 设置attributedPlaceholder属性

      textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"placeholder" attributes:@{NSForegroundColorAttributeName: [UIColor darkGrayColor], NSFontAttributeName: [UIFont systemFontOfSize:13]}];
      
    • 重写UITextField子类的drawPlaceholderInRect:方法

      - (void)drawPlaceholderInRect:(CGRect)rect {
      
        [self.placeholder drawInRect:rect withAttributes:@{NSForegroundColorAttributeName: [UIColor purpleColor], NSFontAttributeName: [UIFont systemFontOfSize:13]}];
      }
      

    适配iOS 13时,可根据实际情况选取后两种方案解决闪退问题。如果项目中重复使用了同一种UITextField的样式,推荐使用第三种方案,创建UITextField的子类。

    修改建议: 采用第二种方案,创建UITextFieldCategory文件,里面封装好修改placeholder的方法,后续修改都可统一直接调用这些方法

    // UITextField+CIPlaceholder.m文件
    
    #import "UITextField+CIPlaceholder.h"
    
    @implementation UITextField (CIPlaceholder)
    
    - (void)setPlaceholderFont:(UIFont *)font {
    
        [self setPlaceholderColor:nil font:font];
    }
    
    - (void)setPlaceholderColor:(UIColor *)color {
    
        [self setPlaceholderColor:color font:nil];
    }
    
    - (void)setPlaceholderColor:(nullable UIColor *)color font:(nullable UIFont *)font {
    
        if ([self checkPlaceholderEmpty]) {
            return;
        }
    
        NSMutableAttributedString *placeholderAttriString = [[NSMutableAttributedString alloc] initWithString:self.placeholder];
        if (color) {
            [placeholderAttriString addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, self.placeholder.length)];
        }
        if (font) {
            [placeholderAttriString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, self.placeholder.length)];
        }
    
        [self setAttributedPlaceholder:placeholderAttriString];
    }
    
    - (BOOL)checkPlaceholderEmpty {
    
        return IsStrEmpty(self.placeholder);
    }
    
    @end
    
  • 子视图leftViewrightView显示异常

    修改建议:将需要显示的视图包装在一个简单的UIView中或者在需要显示的自定义视图子类里,实现systemLayoutSizeFittingSize:方法

    //  示例代码
    - (void)buildTextField {
        UITextField *textField;
        //  iOS 13之前
        textField.rightView = [self buildButtonForRightView];
        //  iOS 13之后
        textField.rightView = [self buildTextFieldRightView];
    }
    
    //  iOS 13之前正常
    - (UIButton *)buildButtonForRightView {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        //  do somethings
        return button;
    }
    
    • 将显示的视图包装在一个简单的UIView

      //  iOS 13之后需要封装一层
      - (UIView *)buildTextFieldRightView {
          UIButton *button = [self buildButtonForRightView];
          //  封装一层
          UIView *rightView = [[UIView alloc] initWithFrame:button.bounds];
          [rightView addSubview:button];
      
          return rightView;
      }
      
    • 在需要显示的自定义视图子类里,实现systemLayoutSizeFittingSize:方法

      //  CICustomButton.m文件
      #import "CICustomButton.h"
      
      @implementation CICustomButton
      
      //  iOS 13之后需要实现该方法
      - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize {
          return CGSizeMake(100, 30);
      }
      
      @end
      
      ------------------------
      //  Build TextFiled页面
      
      //  iOS 13之后,修改UIButton为CICustomButton
      - (UIButton *)buildButtonForRightView {
          UIButton *button = [CICustomButton buttonWithType:UIButtonTypeRoundedRect];
          //  do somethings
          return button;
      }
      

2. UISearchBar

通过valueForKeysetValue: forKeyPath获取和设置私有属性程序崩溃
//  修改searchBar的textField
UISearchBar *searchBar = [[UISearchBar alloc] init];

//  iOS 13之前可直接获取,然后修改textField相关属性
UITextField *searchTextField = [searchBar valueForKey:@"_searchField"];

修改建议:可遍历searchBar的所有子视图,找到指定的UITextField类型的子视图,根据上述UITextField的相关方法修改属性;也可根据UITextField自定义UISearchBar的显示

//  UISearchBar+CIChangePrivateSubviews.m文件

//  修改SearchBar的系统私有子视图

#import "UISearchBar+CIChangePrivateSubviews.h"

@implementation UISearchBar (CIChangePrivateSubviews)

/// 修改SearchBar系统自带的TextField
- (void)changeSearchTextFieldWithCompletionBlock:(void(^)(UITextField *textField))completionBlock {

    if (!completionBlock) {
        return;
    }

    UITextField *textField = [self findTextFieldWithView:self];
    if (textField) {
        completionBlock(textField);
    }
}

/// 递归遍历UISearchBar的子视图,找到UITextField
- (UITextField *)findTextFieldWithView:(UIView *)view {

    for (UIView *subview in view.subviews) {
        if ([subview isKindOfClass:[UITextField class]]) {
            return (UITextField *)subview;
        }else if (!IsArrEmpty(subview.subviews)) {
            return [self findTextFieldWithView:subview];
        }
    }
    return nil;
}

@end

3. UITableView

iOS 13设置cell.contentView.backgroundColor会影响cellselected或者highlighted时的效果

例如:如果设置cell.selectedBackgroundView为自定义选中背景视图,并修改cell.contentView.backgroundColor为某种不透明颜色;contentView就会遮盖cell.selectedBackgroundView,最终导致无法看到自定义的selectedBackgroundView效果。

修改建议:不设置contentView.backgroundColor时,默认值为nil;改为直接设置cell本身背景色

//  自定义cell.m文件

//  iOS 13之前正常
self.contentView.backgroundColor = [UIColor blueColor];
//  iOS 13改为
self.backgroundColor = [UIColor blueColor];

备注:iOS 13UITableView还做了一些其他的修改,详细内容可查阅最底部 参考内容1,整个网页搜索UITableViewCell即可

4. UITabbar

  • Badge文字显示不正常

    Badge默认字体大小,iOS 13从之前13号字体变为17号字体

    修改建议:在初始化TabBarController时,在需要显示BadgeViewController处调用setBadgeTextAttributes:forState:方法

    //  iOS 13需要添加
    if (@available(iOS 13, *)) {
        [viewController.tabBarItem setBadgeTextAttributes:@{NSFontAttributeName: CI_FONT_13} forState:UIControlStateNormal];
        [viewController.tabBarItem setBadgeTextAttributes:@{NSFontAttributeName: CI_FONT_13} forState:UIControlStateSelected];
    }
    
  • 字体颜色异常

    iOS 13不设置self.tabBar.barTintColor = [UIColor clearColor];时字体颜色会显示蓝色,iOS 13之前设置与否无影响

    修改建议:设置tabBar.barTintColor颜色为UIColor clearColor]

    //  自定义TabBarController.m文件
    - (void)viewDidLoad {
      [super viewDidLoad];
    
      self.tabBar.barTintColor = [UIColor clearColor];
      //  整个项目只有一种TabBarController时或者多个相同风格时可设置全局风格(其他情况不推荐)
      // [[UITabBar appearance] setBarTintColor:[UIColor clearColor]];
    }
    

5. UITabBarItem

  • 播放gif,设置ImageView图片时注意设置图片的scale比例
    //  根据路径取gif数据
    NSData *data = [NSData dataWithContentsOfFile:path];
    CGImageSourceRef gifSource = CGImageSourceCreateWithData(CFBridgingRetain(data), nil);
    size_t gifCount = CGImageSourceGetCount(gifSource);
    CGImageRef imageRef = CGImageSourceCreateImageAtIndex(gifSource, i,NULL);
    
    //  iOS 13之前
    UIImage *image = [UIImage imageWithCGImage:imageRef]
    //  iOS 13需要注意:添加scale比例(该imageView将展示该动图效果)
    UIImage *image = [UIImage imageWithCGImage:imageRef scale:image.size.width / CGRectGetWidth(imageView.frame) orientation:UIImageOrientationUp];
    
    CGImageRelease(imageRef);
    
  • 播放gif,需找到设置的ImageView视图
    //  UITabBarItem+CIChangePrivateSubviews.m文件
    //  修改TabbarItem系统私有子视图
    
    #import "UITabBarItem+CIChangePrivateSubviews.h"
    
    @implementation UITabBarItem (CIChangePrivateSubviews)
    
    /// 修改UITabBarSwappableImageView
    - (void)changeSwappableImageViewWithCompletionBlock:(void(^)(UIImageView *imageView))completionBlock {
    
        if (!completionBlock) {
            return;
        }
    
        UIView *tabBarButton = [self valueForKey:@"view"];
        for (UIView *subviews in tabBarButton.subviews) {
            if ([NSStringFromClass([subviews class]) isEqualToString:@"UITabBarSwappableImageView"]) {
                completionBlock((UIImageView *)subviews);
            }
        }
    }
    
  • 只显示图片,没有文字时图片的位置调整

    iOS 13不需要调整imageInsets,图片会自动居中显示,如果设置会造成图片位置有些许偏移

    修改建议:添加版本限制条件,只在iOS 13之前调用设置方法

    if (IOS_VERSION < 13.0) {
        viewController.tabBarItem.imageInsets = UIEdgeInsetsMake(5, 0, -5, 0);
    }
    

6. 弹出ViewController样式变化

模态展示UIModalPresentationStyle类型新增UIModalPresentationAutomatic API_AVAILABLE(ios(13.0)) = -2

用户调用presentViewController:animated:completion:方法弹出视图时,iOS 13效果变化更炫酷,可以在iOS 13系统App中体验到这种变化;
如果不希望使用这种效果,可利用Runtime方法,恢复设置modalPresentationStyleUIModalPresentationFullScreen

//  UIViewController+CIChangePresentStyle.m文件

#import "UIViewController+CIChangePresentStyle.h"

@implementation UIViewController (CIChangePresentStyle)

+ (void)load {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        //替换方法
        SEL originalSelector = @selector(presentViewController:animated:completion:);
        SEL swizzledSelector = @selector(ci_presentViewController:animated:completion:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);;
        BOOL didAddMethod =
        class_addMethod(class,
                        originalSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));

        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)ci_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {

    viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
    [self ci_presentViewController:viewControllerToPresent animated:flag completion:completion];
}

@end

7. 第三方SDK相关

  • 友盟社会化分享SDK:使用“新浪微博完整版”闪退

    替换新浪微博最新的SDK版本Github地址,友盟集成SDK暂时未更新

  • 高德地图相关等其他第三方SDK更新

    查看相关Github或者官方SDK下载地址,更新最新的SDK即可

以上就是适配iOS 13的一些修改建议。如果各位朋友发现有一些新问题和解决方案,可以在评论区留言,希望大家畅所欲言,共同发现并帮助解决问题。

参考内容

  1. iOS & iPadOS 13 Beta 6 Release Notes
  2. 友盟+推出全新SDK,适配iOS 13
  3. 新浪微博 iOS SDK
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容