ios适配_ios适配h5 - CSDN
精华内容
参与话题
  • iOS屏幕适配

    千次阅读 2018-04-19 17:00:10
    最近准备学习OC,先简单地搜索一下屏幕适配,发现没有特别省事的适配方法,决定按照android适配的方法简单地写一个适配方案。希望有人可以受到启发,写出一套懒人适配方案,还请记得告诉我一下,谢谢。另外,由于接触...

    最近准备学习OC,先简单地搜索一下屏幕适配,发现没有特别省事的适配方法,决定按照android适配的方法简单地写一个适配方案。希望有人可以受到启发,写出一套懒人适配方案,还请记得告诉我一下,谢谢。另外,由于接触OC不久,所以很多常识性的东西都还不懂,请见谅。

    来到正文,android手机的屏幕千奇百怪,推荐鸿洋大神的百分比自动适配方案:
    http://blog.csdn.net/lmj623565791/article/details/49990941
    也有其他大神精简的方案:
    http://blog.csdn.net/zhengjingle/article/details/51742839
    可以根据自己的需求适当改写。

    核心原理

    原则上来说知道了控件宽度和高度以及左边距和上边距就可以确定一个控件。
    所以我们只需要根据设计图的宽高与实际屏幕宽高比例对各种控件的宽度、高度、上下左右边距进行缩放就可以完成初步布局,再加上字体、内边距、圆角大小等就可以完美实现布局效果。

    目前我选取的方式是使用storyboard拖拽控件布局,因为拖拽很快。

    首先假设我们的设计图是750x1334,然后我们根据设计图拖拽控件,设置好控件的宽度和高度,然后设置依赖关系即可。上面提到我们只需要设置宽度、高度、左边距和上边距就可以确定一个控件。我们直接往默认的ViewController对应的main.storyboard上拖拽一个UIButton。设置宽度为200,高度100,距离屏幕左边40,屏幕顶部50。

    首先获取屏幕宽度与高度,6s的屏幕尺寸也是750x1334,在6s上的宽度缩放比例wRate =750 /750=1,高度缩放比例hRate=1334/1334=1。SE的屏幕尺寸是640x1138,宽度缩放比例wRate=640/750,高度缩放比例hRate=1138/1334。

    高度和宽度

    我们首先处理View的宽度与高度。每一个view都有宽度与高度,如果明确指定了宽度与高度的约束,那么我们就可以拿到宽度与高度的值;如果没有指定,我们拿不到宽度与高度的值,那么我们不需要处理,系统会处理好。

    比如我们指定一个buttonA,宽度50,高度60,左边距40,上边距50,那么我们就可以通过NSLayoutAttributeWidth和NSLayoutAttributeHeight约束拿到50和60。
    我们在指定buttonB,高度50,上边距50,左边距50,右边距50,我们可以通过NSLayoutAttributeHeight拿到高度50,我们只要改变左边距和右边距的值,系统会自动处理宽度。

     //所有的view都需要适配宽高
    NSArray* constrains = view.constraints;
    for (NSLayoutConstraint* constraint in constrains) {
        CGFloat temp =constraint.constant;        
        if(temp!=0){
            if (constraint.firstAttribute == NSLayoutAttributeHeight) {
                printf("适配前高度的约束--%f\n",constraint.constant);
                constraint.constant  = temp * hRate;
                printf("适配后高度的约束--%f\n",constraint.constant);
            }else if(constraint.firstAttribute == NSLayoutAttributeWidth){
                printf("适配前宽度的约束--%f\n",constraint.constant);
                constraint.constant  = temp * wRate;
                printf("适配后宽度的约束--%f\n",constraint.constant);
            }
        }
    }
    

    上下左右边距

    然后处理VIew的上下左右边距,我们把所有的边距处理都交给父View,在父View里可以得到所有的依赖关系。

    NSArray* constrains = view.constraints;
    for (NSLayoutConstraint* constraint in constrains) {
        CGFloat temp=constraint.constant;
        if(temp!=0){
            //这里只需要判断firstAttribute即可
            //因为leading只可能跟leading或者trailing有关系,都是水平的
            if (constraint.firstAttribute == NSLayoutAttributeLeading
                || constraint.firstAttribute == NSLayoutAttributeTrailing) {
                printf("find width\n");
                constraint.constant = temp * wRate;//左右水平缩放
            }else if(constraint.firstAttribute ==NSLayoutAttributeTop
                     ||constraint.firstAttribute == NSLayoutAttributeBottom){
                       printf("find heihgt\n");
                constraint.constant  = temp * hRate;//上下竖直缩放
            }
        }
    }
    

    通过处理View的宽度、高度、上下左右边距就可以确定View。当然绘制View的时候系统可能会对小数点进行截断,比如button缩放之后的宽度可能是200.66666667,系统只绘制了200.5,如果布局要求特别精准的话需要特别处理。

    从1到100

    上面我们已经可以确定一个View的位置以及尺寸,接下来我们要做的就是确定每一个View的位置以及尺寸。
    我们首先判断一个view是否有子view ,如果没有子View,那么很简单,只需要管好自己就好,直接宽高按比例缩放即可;如果有子View,那么不仅需要对自身宽高进行缩放,还需要缩放子View之间的margin以及子View与父View之间的上下左右边距,然后处理每一个子View。一直递归,直到全部处理完毕。

    我们新建一个工程,默认的是ViewController.m。我们拖拽一个UIButton到main.storyboard,设置宽度200,高度100,左边距40,上边距50,传入viewController.view,首先缩放viewController.view的宽度与高度。然后缩放viewController.view与UIButton的依赖。
    处理viewController.view之后,处理UIButton,先处理UIButton的宽度与高度,由于UIButton没有子View,就不需要再处理对应的子View之间的依赖关系以及父子View之间的对应关系。
    如果在6s上,最后UIButton的宽度是200 * wRate=200 * 1=200,高度是100* hRate=100 * 1=100,左边距是40*wRate =40*1 =40,上边距是50 * hRate=50*1=50。
    如果在SE上,最后UIButton宽度是200 * wRate=200*640/750, 高度是100 * hRate=100 * 1138/1334,左边距是40 * wRate= 40 * 640 /750,上边距是50 * 1138 / 1334。

    至此,View的位置、大小都已经处理完毕。然后我们还需要处理字体、内边距、圆角等。

    适配字体

    UIButton、UITextView、UILabel、UITextField等需要适配字体,这些和宽高一样属于View的自身属性,与其他的View没有关系。所以可以和处理宽高写在一起。

        if([view isKindOfClass:[UIButton class]]){
            UIButton *button=(UIButton *)view;
            [self autoUIButton:button:wRate:hRate];
        }else if([view isKindOfClass:[UILabel class]]){
            UILabel *label = (UILabel *)view;
            [self autoUILabel:label:wRate :hRate];
        }else if([view isKindOfClass:[UITextField class]]){
            UITextField *textField = (UITextField *)view;
            [self autoUITextField:textField :wRate :hRate];
        }else if([view isKindOfClass:[UITextView class]]){
            UITextView *textView = (UITextView *)view;
            [self autoTextView:textView :wRate :hRate];
        }else if([view isKindOfClass:[UIImageView class]]){
            UIImageView *imageView = (UIImageView *)view;
            [self autoUIImageView:imageView :wRate :hRate];
        }
    

    对每一种View需要单独处理:比如UIButton、UITextView等需要处理字体大小。
    UIButton适配字体:
    +(void)autoUIButton:(UIButton *)button:(float)wRate:(float)hRate{
    //适配字体
    textRate = wRate;
    float textSize =button.titleLabel.font.pointSize * textRate;
    button.titleLabel.font = [UIFont systemFontOfSize: textSize];
    }
    UITextView字体适配:
    +(void)autoTextView:(UITextView *)textView :(float)wRate :(float)hRate{
    //适配字体
    textRate = hRate;
    float textSize =textView.font.pointSize * textRate;
    textView.font =[UIFont systemFontOfSize: textSize];
    }
    这里字体适配可以根据自己的需要来选择字体是根据宽度、高度或者对角线的缩放比例来缩放,按照最终效果适当调整。

    图片处理

    iOS在设置图片宽高时可以直接指定宽高的比例,这一点很赞。不用担心原本宽高一样的图片缩放后变形。

    状态栏处理

    由于iphoneX的状态栏高度和之前的手机状态栏不一样,所以适配时需要考虑到状态栏高度。可以把原本的设计图尺寸高度减去状态栏的高度当作标准,比如原本750*1334的设计图,减去状态栏高度后就是750 * 1294,当然一直对应获取屏幕高度是也要减去状态栏高度,这样缩放比例才是正常的。

    导航栏处理

    和状态栏处理类似,减去系统导航栏的高度再进行缩放。没有测试过每一个手机的导航栏高度是多少,之后会测试一下每一个手机。如果有tabbar的话,处理也是类似。

    后续改进

    至此我们已经可以完成基本布局的适配, 后续还需要适配UIButton等的背景图片,距离水平中心20等特殊属性,性能方面还需要测试。

    有些东西没说明白的,可以去通过上面的连接去查看鸿洋大神的介绍,虽然是Android,不过道理是一样的。

    代码很乱,之后会改进,轻喷。

    //
    // AutoUtils.h
    // ScreenPercent
    //
    // Created by D on 2018/2/1.
    // Copyright © 2018年 D. All rights reserved.
    //

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #import <math.h>
    
    @interface AutoUtils : NSObject
    
    @property float designWidth;//设计宽
    @property float designHeight;//设计高
    @property float _wRate;//宽拉伸比例
    @property float _hRate;//高拉伸比例
    
    
    /**
     初始化
     todo  加入对tabbar的处理
     @param designWidth 设计图尺寸的宽度
     @param designHeight 设计图尺寸的高度(不包含状态栏)
     */
    +(void)initAuto:(float) designWidth:(float) designHeight;
    
    
    /**
     适配UIViewController
     这个方法主要是为了之后处理UINavigationBar、UITabBar
     */
    +(void)autoUIViewController:(UIViewController *)viewController;
    
    /**
    
     */
    
    /**
     适配UIViewController
     这个方法主要是为了之后处理UINavigationBar、UITabBar
    
     @param viewController UIViewController对象
     @param hasNavigationBar 是否有NavigationBar
     @param hasTabBar 是否有TabBar
     */
    +(void)autoUIViewController:(UIViewController *)viewController
                               :(Boolean)hasNavigationBar
    :(Boolean)hasTabBar;
    
    
    /**
     获取navigationbar高度
    
     @return float
     */
    +(float)getNavigationBarHeight:(UIViewController *)viewController;
    
    /**
     获取tabbar高度
    
     @return float
     */
    +(float)getTabBarHeight:(UIViewController *)viewController;
    
    /**
     适配普通的View
    
     @param view void
     */
    +(void)autoView:(UIView *)view;
    
    /**
     缩放后的宽度
    
     @param viewWidth 要缩放View的宽度
     @param designWidth 设计图的宽度尺寸  例如375
     @return <#return value description#>
     */
    +(float)getDisplayWidth:(float) viewWidth:(float) designWidth;
    
    /**
     Description 缩放后的高度
    
     @param viewHeight 要缩放的View的高度
     @param designHeight 设计图的高度尺寸,去掉状态之后的高度  例如647
     @return <#return value description#>
     */
    +(float)getDisplayHeight:(float) viewHeight:(float) designHeight;
    /**
     含有子View的View,需要处理子View之间以及子View与父View的距离
     */
    +(void)autoRelatives:(UIView *)view:(float)wRate:(float)hRate;
    
    /**
     不含有子View的View 处理自身的宽高信息
    
     @param view <#view description#>
     */
    +(void)autoSelf:(UIView *)view:(float)wRate:(float)hRate;
    
    /**
     适配button
    
     @param button 要适配的button
     @param wRate 水平方向拉伸比例
     @param hRate 竖直方向拉伸比例
     */
    +(void)autoUIButton:(UIButton *)button:(float)wRate:(float)hRate;
    
    /**
    
     适配textView
     @param textView 要适配的textView
     @param wRate 水平方向拉伸比例
     @param hRate 竖直方向拉伸比例
     */
    +(void)autoTextView:(UITextView *)textView:(float)wRate:(float)hRate;
    
    /**
     适配label
    
     @param label 要适配的label
     @param wRate 水平方向拉伸比例
     @param hRate 竖直方向拉伸比例
     */
    +(void)autoUILabel:(UILabel *)label:(float)wRate:(float)hRate;
    
    /**
     适配textField
    
     @param textField 要适配的textField
     @param wRate 水平方向拉伸比例
     @param hRate 竖直方向拉伸比例
     */
    +(void)autoUITextField:(UITextField *)textField:(float)wRate:(float)hRate;
    
    /**
     适配imageView
     如果宽高都按照宽缩放,那么固定宽度(如50),高度通过设置aspect ratio
     同理宽高都按照高缩放,那么固定高度(如50),宽度通过设置aspect ratio
     如果宽按宽缩放,高按照高缩放,那么设置宽高的值即可
    
     @param textField 要适配的imageView
     @param wRate 水平方向拉伸比例
     @param hRate 竖直方向拉伸比例
     */
    +(void)autoUIImageView:(UIImageView *)imageView:(float)wRate:(float)hRate;
    @end
    
    
    //
    //  AutoUtils.m
    //  ScreenPercent
    //
    //  Created by D on 2018/2/1.
    //  Copyright © 2018年 D. All rights reserved.
    //
    
    
    #import "AutoUtils.h"
    
    
    //默认的设计图尺寸
    static float designWidth = 375.0;
    static float designHeight = 667.0;
    //屏幕显示区域的宽高
    static float displayWidth = 0;
    static float displayHeight = 0;
    
    
    //只去除状态栏的高度后的缩放比例
    static float designWidthRate = 0;
    static float designHeightRate = 0;
    
    static float textRate = 0;
    
    
    //暂时不考虑navigationbar和tabbar等的影响,目前只适配基本控件
    
    @implementation AutoUtils
    
    +(void)initAuto:(float)designW:(float)designH{
    
        //设计图尺寸
        designWidth = designW;
        designHeight = designH;
    
        //导航栏高度
        displayWidth =[UIScreen mainScreen].bounds.size.width;
        displayHeight = [UIScreen mainScreen].bounds.size.height;
    
        //计算缩放比例
        designWidthRate = displayWidth / designWidth;
        designHeightRate= displayHeight / designHeight;
        textRate =displayWidth /designWidth;
    //    textRate =hypotf(displayWidth,displayHeight) /hypotf(designWidth,designHeight);
    }
    +(void)autoUIViewController:(UIViewController *)viewController{
        [self autoUIViewController:viewController :false :false];
    }
    
    +(void)autoUIViewController:(UIViewController *)viewController :(Boolean)hasNavigationBar :(Boolean)hasTabBar{
        float navigationBarHeight = 0;
        float tabBarheight = 0;
        if(hasNavigationBar){
            navigationBarHeight = [self getNavigationBarHeight:viewController];
        }
        if(hasTabBar){
            tabBarheight = [self getTabBarHeight:viewController];
        }
        [self autoSelf:viewController.view:designWidthRate:designHeightRate];
        [self autoRelatives:viewController.view:designWidthRate:designHeightRate];
    //
    }
    +(void)autoView:(UIView *)view{
        [self autoSelf:view:designWidthRate:designHeightRate];//处理自己的宽高
        //有子View
        [self autoRelatives:view:designWidthRate:designHeightRate];//处理
    }
    
    +(void)autoRelatives:(UIView *)view :(float)wRate :(float)hRate{
    
        //必须有子View
        NSArray *subviews = [view subviews];
          if ([subviews count] == 0){
              return;
          }
        //适配子View之间的间距,适配子View与自己的间距
        //目前只处理上下左右的间距
        NSArray* constrains = view.constraints;
    //    printf("最外层的contrains count--%lu\n",constrains.count);
    //    printf("NSLayoutAttributeLeft%lu\n",NSLayoutAttributeLeft);
    //printf("NSLayoutAttributeRight%lu\n",NSLayoutAttributeRight);    printf("NSLayoutAttributeTop%lu\n",NSLayoutAttributeTop);    printf("NSLayoutAttributeBottom%lu\n",NSLayoutAttributeBottom);    printf("NSLayoutAttributeLeading%lu\n",NSLayoutAttributeLeading);    printf("NSLayoutAttributeTrailing%lu\n",NSLayoutAttributeTrailing);    printf("NSLayoutAttributeWidth%lu\n",NSLayoutAttributeWidth);    printf("NSLayoutAttributeHeight%lu\n",NSLayoutAttributeHeight);    printf("NSLayoutAttributeCenterX%lu\n",NSLayoutAttributeCenterX);
    //printf("NSLayoutAttributeCenterY%lu\n",NSLayoutAttributeCenterY);
    //    printf("NSLayoutAttributeLastBaseline%lu\n",NSLayoutAttributeLastBaseline);
    //
    //  printf("stop\n");
        for (NSLayoutConstraint* constraint in constrains) {
    //        printf("%lu--%lu--%f\n",constraint.firstAttribute,constraint.secondAttribute,constraint.constant);
            CGFloat temp=constraint.constant;
            if(temp!=0){
                //这里只需要判断firstAttribute即可
                //因为leading只可能跟leading或者trailing有关系,都是水平的
                if (constraint.firstAttribute == NSLayoutAttributeLeading
                    || constraint.firstAttribute == NSLayoutAttributeTrailing) {
                    printf("find width\n");
                    constraint.constant = temp * wRate;//左右水平缩放
                }else if(constraint.firstAttribute ==NSLayoutAttributeTop
                         ||constraint.firstAttribute == NSLayoutAttributeBottom){
                           printf("find heihgt\n");
                    constraint.constant  = temp * hRate;//上下竖直缩放
                }
            }
        }
    
        //接着循环遍历处理每一个子View
        for (UIView *subview in subviews) {
            [self autoView:subview];
        }
    }
    
    +(void)autoSelf:(UIView *)view:(float)wRate:(float)hRate{
    
    
        printf("------View自己的属性------\n");
        printf("NSLayoutAttributeLeft%lu\n",NSLayoutAttributeLeft);
        printf("NSLayoutAttributeRight%lu\n",NSLayoutAttributeRight);    printf("NSLayoutAttributeTop%lu\n",NSLayoutAttributeTop);    printf("NSLayoutAttributeBottom%lu\n",NSLayoutAttributeBottom);    printf("NSLayoutAttributeLeading%lu\n",NSLayoutAttributeLeading);    printf("NSLayoutAttributeTrailing%lu\n",NSLayoutAttributeTrailing);    printf("NSLayoutAttributeWidth%lu\n",NSLayoutAttributeWidth);    printf("NSLayoutAttributeHeight%lu\n",NSLayoutAttributeHeight);    printf("NSLayoutAttributeCenterX%lu\n",NSLayoutAttributeCenterX);
        printf("NSLayoutAttributeCenterY%lu\n",NSLayoutAttributeCenterY);
            printf("NSLayoutAttributeLastBaseline%lu\n",NSLayoutAttributeLastBaseline);
    
        //所有的view都需要适配宽高
        NSArray* constrains = view.constraints;
          printf("最外层的contrains count--%lu\n",constrains.count);
        for (NSLayoutConstraint* constraint in constrains) {
            CGFloat temp =constraint.constant;
            printf("constant is %ld =%f\n",constraint.firstAttribute,temp);
            if(temp!=0){
                if (constraint.firstAttribute == NSLayoutAttributeHeight) {
                    printf("适配前高度的约束--%f\n",constraint.constant);
                    constraint.constant  = temp * hRate;
                    printf("适配后高度的约束--%f\n",constraint.constant);
                }else if(constraint.firstAttribute == NSLayoutAttributeWidth){
                    printf("适配前宽度的约束--%f\n",constraint.constant);
                    constraint.constant  = temp * wRate;
                    printf("适配后宽度的约束--%f\n",constraint.constant);
                }
            }
        }
    
        //是否button
        if([view isKindOfClass:[UIButton class]]){
            UIButton *button=(UIButton *)view;
            [self autoUIButton:button:wRate:hRate];
                  printf("------以上时UIButton自己的属性------\n");
        }else if([view isKindOfClass:[UILabel class]]){
            UILabel *label = (UILabel *)view;
            [self autoUILabel:label:wRate :hRate];
                          printf("-----以上时UILabel自己的属性------\n");
        }else if([view isKindOfClass:[UITextField class]]){
            UITextField *textField = (UITextField *)view;
            [self autoUITextField:textField :wRate :hRate];
                  printf("------以上时UITextField自己的属性------\n");
        }else if([view isKindOfClass:[UITextView class]]){
            UITextView *textView = (UITextView *)view;
            [self autoTextView:textView :wRate :hRate];
                printf("------以上是UITextView自己的属性------\n");
        }else if([view isKindOfClass:[UIImageView class]]){
            UIImageView *imageView = (UIImageView *)view;
            [self autoUIImageView:imageView :wRate :hRate];
            printf("------以上是UIImageView自己的属性------\n");
        }
        }
    
        +(float)getDisplayWidth:(float)viewWidth :(float)designWidth{
            return viewWidth *  displayWidth /  designWidth;
        }
        +(float)getDisplayHeight:(float)viewHeight :(float)designHeight{
            return viewHeight * displayHeight / designHeight;
        }
    
        /**
         目前不考虑Navigationbar的影响
         之后减去navigationbar高度
         或者对navigationbar的高度进行适配
    
         @param viewController viewController
         @return 0
         */
        +(float)getNavigationBarHeight:(UIViewController *)viewController{
            return 0;
        // return viewController.navigationController.navigationBar.frame.size.height;
        }
    
        /**
         目前不考虑tabbar的影响
         之后减去tabbar高度
         或者对tabbar的高度进行适配
    
         @param viewController viewController
         @return 0
         */
        +(float)getTabBarHeight:(UIViewController *)viewController{
            return 0;
        //    return viewController.tabBarController.tabBar.frame.size.height;
        }
    
        +(void)autoUIButton:(UIButton *)button:(float)wRate:(float)hRate{
            textRate = wRate;
            printf("适配button\n");
            printf("button child view 个数 %ld\n", button.subviews.count);
    
            //获取约束
            NSArray* constrains = button.constraints;
    
            printf("contrains count--%lu\n",constrains.count);
    
            //适配字体
            float textSize =button.titleLabel.font.pointSize * textRate;
            button.titleLabel.font = [UIFont systemFontOfSize: textSize];
    
        }
    
        +(void)autoUILabel:(UILabel *)label :(float)wRate :(float)hRate{
           textRate = wRate;
            printf("适配label\n");
            printf("label child view 个数 %ld\n", label.subviews.count);
            //适配字体
            float textSize =label.font.pointSize * textRate;
            label.font = [UIFont systemFontOfSize: textSize];
    
        }
        +(void)autoTextView:(UITextView *)textView :(float)wRate :(float)hRate{
            textRate = wRate;
            printf("适配textView\n");
            printf("textView child view 个数 %ld\n", textView.subviews.count);
            //适配字体
            float textSize =textView.font.pointSize * textRate;
            textView.font =[UIFont systemFontOfSize: textSize];
        }
        +(void)autoUITextField:(UITextField *)textField :(float)wRate :(float)hRate{
            textRate = wRate;
            printf("适配textField\n");
            printf("textField child view 个数 %ld\n", textField.subviews.count);
            //适配字体
            float textSize =textField.font.pointSize * textRate;
            textField.font =[UIFont systemFontOfSize: textSize];
    
        }
    
        +(void)autoUIImageView:(UIImageView *)imageView :(float)wRate :(float)hRate{
    
        }
        @end
    

    调用
    - (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.
    
    
    [AutoUtils initAuto:375.0:647.0];
    [AutoUtils autoUIViewController:self :false :false]; 
    

    }

    tableView适配
    在contentView套一层UIView,指定contentView与UIView高度一致,通过指定UIView的高度来设置contentView的高度。

    获取cell的时候对cell进行适配即可。
    [AutoUtils autoView:cell];

    展开全文
  • iOS13 适配总结

    千次阅读 2019-11-14 14:53:20
    1、UISegmentedControl 修改选择背景色 if (@available(iOS 13.0, *)) { _segmented.selectedSegmentTintColor = Stock_Red; } else { _segmented.tintColor = Stock_Red; }

    1、UISegmentedControl 修改选择背景色

           if (@available(iOS 13.0, *)) {
                _segmented.selectedSegmentTintColor = Stock_Red;
            } else {
                _segmented.tintColor = Stock_Red;
            }
    // 以下代码可以代替上面:
       [_segmented setBackgroundImage:[UIImage imageWithColor:[UIColor clearColor]] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
       [_segmented setBackgroundImage:[UIImage imageWithColor:Stock_Red] forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
    
    // [UIImage imageWithColor:xx]
    // 这是我自己封装的方法:返回一张纯色图片 
    + (UIImage *)imageWithColor:(UIColor *)color {
        CGRect rect = CGRectMake(0, 0, 1, 1);
        UIGraphicsBeginImageContext(rect.size);
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetFillColorWithColor(context, [color CGColor]);
        CGContextFillRect(context, rect);
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    }
    
    



    2、模态视图样式

    代码块语法遵循标准markdown代码,例如:

    要改回原来模态视图样式,我们只需要把UIModalPresentationStyle设置为UIModalPresentationFullScreen即可。
    
    ViewController *vc = [[ViewController alloc] init];
    vc.title = @"presentVC";
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
    nav.modalPresentationStyle = UIModalPresentationFullScreen;
    [self.window.rootViewController presentViewController:nav animated:YES completion:nil];
    



    3、私有KVC

    在使用iOS 13运行项目时突然APP就crash掉了。定位到的问题是在设置UITextField的Placeholder也就是占位文本的颜色和字体时使用了KVC的方法:

    [_textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
    [_textField setValue:[UIFont systemFontOfSize:14] forKeyPath:@"_placeholderLabel.font"];
    

    可以将其替换为:

    _textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"文本" attributes:@{
    NSFontAttributeName:[UIFont systemFontOfSize:16],
    NSForegroundColorAttributeName:[UIColor redColor]}];
    

    并且只需要在初始化的时候设置attributedPlaceholder即富文本的占位文本,再重新赋值依然使用placeolder直接设置文本内容,样式不会改变。



    4、黑暗模式

    取消黑暗模式
    在 plist 文件中添加一下键值对:
    UIUserInterfaceStyle Light
    在这里插入图片描述



    5、iOS13 屏幕旋转

    这里 ViewController 简称:VC
    iOS13以前的 API (在iOS13中失效了),以前在进入ViewController 时可以自动调用,现在不调用了

    @interface xxViewController
    
    @end
    
    @implementation xxViewController
    
    // 原需求:屏幕锁定,不支持屏幕旋转,手动让VC横屏    
    #pragma mark - Orientation是否支持转屏
    - (BOOL)shouldAutorotate{
        return NO;
    }
    #pragma mark - 支持哪些转屏方向
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations{
        if (self.isBack) {
            return UIInterfaceOrientationMaskPortrait;
        }else {
            return UIInterfaceOrientationMaskLandscapeRight;
        }
    }
    #pragma mark - 默认的屏幕方向
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
        return UIInterfaceOrientationLandscapeRight;
    }
    @end
    

    iOS 13 中的解决方案

    //-----------------------"  AppDelegate  "-----------------------
    #import <UIKit/UIKit.h>
    #import <CoreData/CoreData.h>
    + (AppDelegate *)shareInstance;
    // 旋转屏幕 
    -(void)changeOrientation:(UIInterfaceOrientation)toOrientation;
    @end
    
    
    #import "AppDelegate.h"
    @interface AppDelegate () 
    @end
    
    @implementation AppDelegate
    
    static AppDelegate *_singleInstance;
    +(AppDelegate *)shareInstance {
        return _singleInstance;
    }
    - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
    {
        if(self.allowRotation) {
            return UIInterfaceOrientationMaskLandscape;
        } else {
            return UIInterfaceOrientationMaskPortrait;
        }
    }
    //要解决很简单,只需要在方法函数头部添加几句不会被编译器优化掉的代码即可。
    #pragma mark - 旋转屏幕
    -(void)changeOrientation:(UIInterfaceOrientation)toOrientation {
        NSLog(@"---旋转屏幕= %ld",(long)toOrientation);
      if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
            SEL selector = NSSelectorFromString(@"setOrientation:");
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
            [invocation setSelector:selector];
            [invocation setTarget:[UIDevice currentDevice]];
          
          int val = 0;
          if (toOrientation==UIInterfaceOrientationPortrait) {
              val = UIInterfaceOrientationPortrait;
              
          }else if (toOrientation==UIInterfaceOrientationPortraitUpsideDown){
              val = UIInterfaceOrientationPortraitUpsideDown;
              
          }else if (toOrientation==UIInterfaceOrientationLandscapeLeft){
              val = UIInterfaceOrientationLandscapeRight;
              
          }else if (toOrientation==UIInterfaceOrientationLandscapeRight){
              val = UIInterfaceOrientationLandscapeLeft;
          }
            [invocation setArgument:&val atIndex:2];
            [invocation invoke];
        }
    }
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        self.window.backgroundColor = [UIColor whiteColor];
        [self.window makeKeyAndVisible];
     
        //1.AppDelegate
        _singleInstance = self;
        self.application = application;
        return YES;
    }
    @end
    
    //-----------------------"  xxViewController  "-----------------------
    interface xxViewController
    
    @end
    
    @implementation xxViewController
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        [AppDelegate shareInstance].allowRotation = YES;
        [[AppDelegate shareInstance] changeOrientation:UIInterfaceOrientationLandscapeLeft];
    }
    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        [AppDelegate shareInstance].allowRotation = NO;
         [[AppDelegate shareInstance] changeOrientation:UIInterfaceOrientationPortrait];
    }
    @end
    
    



    6、iOS13 新增编辑手势

    复制:三指捏合
    剪切:两次三指捏合
    粘贴:三指松开
    撤销:三指向左划动(或三指双击)
    重做:三指向右划动
    快捷菜单:三指单击

    // 暂时禁用

    #ifdef __IPHONE_13_0
    - (UIEditingInteractionConfiguration)editingInteractionConfiguration{
        return UIEditingInteractionConfigurationNone;
    }
    #endif
    



    7、iOS13 新生命周期 SceneDelegate

    增加了 #import "SceneDelegate.h"
    原来的 #import "AppDelegate.h" 没有了window

    要想维持原状,需要在AppDelegate里,添加window,并且删掉Info.plist里的新增键值对
    @property (strong, nonatomic) UIWindow *window;

    @interface AppDelegate : UIResponder
    @property (strong, nonatomic) UIWindow *window;
    @end
    删掉以下键值对
    Application Scene Manifest:2times
    Main storyboard file base name:Main

    在这里插入图片描述


    更新中·····

    「文章有不对地方,欢迎批评指正!」
    展开全文
  • iOS 13适配汇总

    千次阅读 2019-09-28 19:13:51
    随着iPhone 11的发布,iOS 13适配也提上了日程,接下来就开发中升级iOS13的手机可能出现的问题 Xcode: 11.0 iOS : 13.0 UIViewController 模态弹出界面 viewController.present(presentVC, animated: true, ...

    随着iPhone 11的发布,iOS 13适配也提上了日程,接下来就开发中升级iOS13的手机可能出现的问题
    Xcode: 11.0
    iOS : 13.0

    UIViewController 模态弹出界面

    viewController.present(presentVC, animated: true, completion: nil)
    在调用模态弹出视图,会发现弹出的界面没有全屏。如图
    模态弹出界面
    通过多次的尝试,发现在低版本里面不会发生这种情况(iOS12及以下),于是我查阅了最新的开发文档,发现了端倪,主要还是因为我们之前忽略了UIViewController里面的一个属性,即:modalPresentationStyle

     Defines the presentation style that will be used for this view controller when it is presented modally. Set this property on the view controller to be presented, not the presenter.
     If this property has been set to UIModalPresentationAutomatic, reading it will always return a concrete presentation style. By default UIViewController resolves UIModalPresentationAutomatic to UIModalPresentationPageSheet, but other system-provided view controllers may resolve UIModalPresentationAutomatic to other concrete presentation styles.
     Defaults to UIModalPresentationAutomatic on iOS starting in iOS 13.0, and UIModalPresentationFullScreen on previous versions. Defaults to UIModalPresentationFullScreen on all other platforms.
    
    
    public enum UIModalPresentationStyle : Int {
        case fullScreen
        
        @available(iOS 3.2, *)
        case pageSheet
        @available(iOS 3.2, *)
        case formSheet
    
        @available(iOS 3.2, *)
        case currentContext
    
        @available(iOS 7.0, *)
        case custom
    
        @available(iOS 8.0, *)
        case overFullScreen
    
        @available(iOS 8.0, *)
        case overCurrentContext
    
        @available(iOS 8.0, *)
        case popover
    
        
        @available(iOS 7.0, *)
        case none
    
        @available(iOS 13.0, *)
        case automatic
    }
    

    通过查看API 可以看到,iOS 13 新增一个:automatic类型,默认情况下就是这个所以才会弹出不是全屏的界面。如果我们想要修改为全屏的话
    可以:presentVC.modalPresentationStyle = .fullScreen设置为全屏即可

    KVC 限制

    iOS13以后已经不能肆无忌惮的通过 KVC来修改一些没有暴露出来的属性了。

    *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Access to xxx's _xxx ivar is prohibited. This is an application bug'
    

    我们常用的有

    // UITextField 的 _placeholderLabel
            let textField = UITextField.init()
            textField.setValue(UIColor.red, forKey: "_placeholderLabel.textColor")
            
            /// UISearchBar 的 _searchField
            [searchBar valueForKey:@"_searchField"]
            
    

    下面方法替换

    ///分别设置字体大小和颜色(富文本)
    textField.attributedPlaceholder = NSAttributedString.init(string: "请输入....", attributes: [NSAttributedString.Key.foregroundColor: UIColor.red], [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)])
    
     /// UISearchBar 用 searchField代替
    bar.value(forKey: "searchField") as! UITextField
    
    

    UISegmentedControl 默认样式改变

    
    默认样式变为白底黑字,如果设置修改过颜色的话,页面需要修改
    

    UITabbar

    如果之前有通过TabBar上图片位置来设置红点位置,在iOS13上会发现显示位置都在最左边去了。遍历UITabBarButton的subViews发现只有在TabBar选中状态下才能取到UITabBarSwappableImageView,解决办法是修改为通过UITabBarButton的位置来设置红点的frame

    App启动过程中,部分View可能无法实时获取到frame

    // 只有等执行完 UIViewController 的 viewDidAppear 方法以后,才能获取到正确的值,在viewDidLoad等地方 frame Size 为 0,例如:
     UIApplication.shared.statusBarFrame
    

    废弃UIWebView

    查看API可以看到:iOS 2.0 到 iOS 11.0
    在12.0就已经被废弃,部分APP使用webview时, 审核被拒

    @available(iOS, introduced: 2.0, deprecated: 12.0, message: "No longer supported; please adopt WKWebView.")
    open class UIWebView : UIView, NSCoding, UIScrollViewDelegate {
    	.........
    	.........
    	.........
    }
    
    

    CNCopyCurrentNetworkInfo

    iOS13 以后只有开启了 Access WiFi Information capability,才能获取到 SSID 和 BSSID wi-fi or wlan 相关使用变更
    最近收到了苹果的邮件,说获取WiFi SSID的接口CNCopyCurrentNetworkInfo 不再返回SSID的值。不仔细看还真会被吓一跳,对物联网的相关APP简直是炸弹。仔细看邮件还好说明了可以先获取用户位置权限才能返回SSID。
    注意:目本身已经打开位置权限,则可以直接获取

    - (NSString*) getWifiSsid {
        if (@available(iOS 13.0, *)) {
            //用户明确拒绝,可以弹窗提示用户到设置中手动打开权限
            if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
                NSLog(@"User has explicitly denied authorization for this application, or location services are disabled in Settings.");
                //使用下面接口可以打开当前应用的设置页面
                //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                return nil;
            }
            CLLocationManager* cllocation = [[CLLocationManager alloc] init];
            if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
                //弹框提示用户是否开启位置权限
                [cllocation requestWhenInUseAuthorization];
                usleep(50);
                //递归等待用户选选择
                return [self getWifiSsidWithCallback:callback];
            }
        }
        NSString *wifiName = nil;
        CFArrayRef wifiInterfaces = CNCopySupportedInterfaces();
        if (!wifiInterfaces) {
            return nil;
        }
        NSArray *interfaces = (__bridge NSArray *)wifiInterfaces;
        for (NSString *interfaceName in interfaces) {
            CFDictionaryRef dictRef = CNCopyCurrentNetworkInfo((__bridge CFStringRef)(interfaceName));
    
            if (dictRef) {
                NSDictionary *networkInfo = (__bridge NSDictionary *)dictRef;
                NSLog(@"network info -> %@", networkInfo);
                wifiName = [networkInfo objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID];
                CFRelease(dictRef);
            }
        }
        CFRelease(wifiInterfaces);
        return wifiName;
    }
    

    打印:如下

     network info -> {
        BSSID = "44:dd:fb:43:91:ff";
        SSID = "Asus_c039";
        SSIDDATA = <41737573 5f633033 39>;
    }
    不同意
    network info -> {
        BSSID = "00:00:00:00:00:00";
        SSID = WLAN;
        SSIDDATA = <574c414e>;
    }
    
    

    SceneDelegate

    有关Xcode 11 新建工程创建SceneDelegate文件,可以参考我的这篇文章:iOS 13 SceneDelegate适配

    iOS 13 暗黑模式

    当我们系统开启暗黑模式的时候,你会发现我们的有些界面白色界面变黑,黑色字体变白。
    原因:我们没有设置背景例如UITableviewCell背景,以及采用UILabel的默认背景黑色,当切换到暗黑模式的时候就会出现上述情况。
    当我们还不打算适配暗黑模式的时候,解决方式1:

    1. 手动设置各个页面背景。显然不太好,麻烦还容易遗漏
    2. 在info.plist中添加一个配置文件就好了:User Interface Style设置为Light

    APP暗黑模式取消
    **注释:**关于如何优雅的适配暗黑模会在后续的博客中更新,敬请期待!
    持续更新中…

    展开全文
  • I、 适配第三方库在iOS13的问题: 1.1) : 升级腾讯的第三方UI框架,解决iOS13 无法访问私有属性的问题 II、 适配iOS13UI控件的API :(涉及的模块有登录) 2.1)UITextField的_placeholderLabel属性:无法直接访问_...

    前言

    汇总本文要点

    1、适配第三方库在iOS13的问题:

    • 1.1) : 升级腾讯的第三方UI框架,解决iOS13 无法访问私有属性的问题

    2、 适配iOS13UI控件的API :(涉及的模块有登录)

    • 2.1)UITextField的_placeholderLabel属性:无法直接访问,使用 富文本的API【 NSMutableAttributedString attributedPlaceholder】
    
     self.endBtn.attributedPlaceholder =[[NSAttributedString alloc] initWithString:models.clearsendStr attributes:    @{NSForegroundColorAttributeName:rgb(153,153,153) ,NSFontAttributeName:self.endBtn.font        }];
         self.numberTF.attributedPlaceholder =[[NSAttributedString alloc] initWithString:self.numberTF.placeholder attributes:    @{NSFontAttributeName:self.numberTF.font        }];
         
     //    [self.numberTF setValue:self.numberTF.font forKeyPath:@"_placeholderLabel.font"];  // iOS13 的系统闪退
    
    
    • 2.2)适配暗黑模式
    • 2.3)iOS 13适配:设置UITabBarItem上title选择和缺省状态的颜色,使用新的API【self.tabBar.unselectedItemTintColor】
    • 2.4)设置状态栏背景颜色的解决方案:使用新的API 【statusBarManager】
    • 2.5)iOS13 适配【present 半屏问题】: 如果没适配会导致列表下拉刷新失效。modalPresentationStyle属性默认不是UIModalPresentationFullScreen了,需要根据需求手动设置:推荐使用UIModalPresentationOverFullScreen
    • 2.6)CNCopyCurrentNetworkInfo:获取WiFi SSID的接口CNCopyCurrentNetworkInfo 不再返回SSID的值,除非先获取用户位置权限才能返回SSID

    I、 适配第三方库在iOS13的问题:

    1.1) : 升级腾讯的第三方UI框架,解决iOS13 无法访问私有属性的问题

    II、 适配iOS13UI控件的API :( 禁止用 KVC访问私有属性)

    同 iOS 13 更新时的 textField 的 placeHolder 属性一样,iOS 14 禁止用 KVC访问 UIPageControl 的私有属性 pageImage

    2.1)UITextField的_placeholderLabel属性:无法直接访问_placeholderLabel.textColor

    解决方案: 使用 NSMutableAttributedString 富文本attributedPlaceholder来替代KVC访问 UITextField 的 _placeholderLabel。

    + (void)setupUITextField4attributedPlaceholder:(UITextField*)textField{
        
                if (@available(iOS 13.0, *)) {
                    
                    
                }else{
                    
                    return;
                }
        
        
        
        textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"placeholder" attributes:@{NSForegroundColorAttributeName:kTextPlaceholderColor, NSFontAttributeName:kTextFont(13) }];
    
        
    //    textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"placeholder" attributes:@{NSForegroundColorAttributeName: [UIColor darkGrayColor], NSFontAttributeName: [UIFont systemFontOfSize:13]}];
    
    }
    
    

    2.2)适配暗黑模式

    2.2.1 关闭暗黑模式

    • 关闭
    <key>UIUserInterfaceStyle</key>
    	<string>Light</string>
    

    2.2.2 暗色模式开发规范

    如果想暗色模式适配起来更方便,就要在开发的时候统一一套暗色模式开发规范;

    • 页面
    • 文本
    • 图片及icon
    • 导航栏
    • 页面卡
    • 状态栏

    当做主题来开发

    1. 首先定制一批颜色:每个颜色都分为亮色模式暗色模式两种
    2. 定义一批图片,亮色和暗色区别
    3. 网络请求图片的处理:如果后台没有这两种,在暗色模式下图片加白边
    4. 导航栏、页面、状态栏、页面卡背景色处理
    5. 其它更多针对每个app的细节修改

    2.3)iOS 13适配:设置UITabBarItem上title颜色

        if (@available(iOS 13.0, *)) {
            self.tabBar.unselectedItemTintColor = ktabNorTextColor;
            
            self.tabBar.tintColor = BASE_RED_COLOR;
    
        }else{
            
            //设置文字样式
            NSMutableDictionary *textAttr = [NSMutableDictionary dictionary];
            textAttr[NSForegroundColorAttributeName] = ktabNorTextColor;
            [childVC.tabBarItem setTitleTextAttributes:textAttr forState:UIControlStateNormal];
            //选择状态的文字颜色样式
            NSMutableDictionary *selectedTextAttr = [NSMutableDictionary dictionary];
            [selectedTextAttr setValue:BASE_RED_COLOR forKey:NSForegroundColorAttributeName];
            [childVC.tabBarItem setTitleTextAttributes:selectedTextAttr forState:UIControlStateSelected];
            
    
            
        }
    
    

    2.3)设置状态栏背景颜色

     [Bugly]  Trapped uncaught exception 'NSInternalInconsistencyException', reason: 'App called -statusBar or -statusBarWindow on UIApplication: this code must be changed as there's no longer a status bar or status bar window. Use the statusBarManager object on the window scene instead.' 
    
    • 旧代码
        UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
    
    
    • 解决方案:使用keyWindow.windowScene.statusBarManager
    if (@available(iOS 13.0, *)) {
           UIView *statusBar = [[UIView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame] ;
            statusBar.backgroundColor = color;
            [[UIApplication sharedApplication].keyWindow addSubview:statusBar];
        } else {
            // Fallback on earlier versions
            UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
               if ([statusBar respondsToSelector:@selector(setBackgroundColor:)]) {
                   statusBar.backgroundColor = color;
               }
        }
    
    
    

    坑2:如果整个项目的状态栏(电池栏)都是有颜色的,并且都是不透明的,上面这个方法完全可以应付

    • 但是:如果有状态是有透明或者半透明的效果,上面这个方法还是不能胜任,越透明越明显;经过一番查找,终于发现问题:

    • 问题: iOS 13之前,可以通过valueForKey 获取UIApplication的statusBar,因为UIApplication是单例,因此,在iOS 12,通过: [[[UIApplication sharedApplication] valueForKey:@“statusBarWindow”] valueForKey:@“statusBar”]拿到的statusBar永远是同一个对象。不行可以打印出内存地址看下就很清楚了。iOS 13之后,因为苹果不允许使用KVC的valueForKey访问私有属性。通过上面的代码可以多看点,每次进来都调用 alloc:init的方法,重新生成一个statusBar;然后添加到UIApplication的keyWindow上,再设置背景颜色。因此这个方法多次调用就会创建多份statusBar,造成内存开销不说,如果设置为透明,根部不能起效果。

    解决办法:既然定位到问题所在,办法就是保证iOS 13 之后,每次也都能拿到有去只有一个对象。方法有很多,我的方法代码如下:使用 static 配合 gcd

    HSSingletonM(QCTStatusBarTool);
    
    + (void)setStatusBarBackgroundColor4ios13:(UIColor *)color {
        
        static UIView *statusBar = nil;
        static dispatch_once_t onceToken;
        if (@available(iOS 13.0, *)) {  //iOS 13不允许使用valueForKey、setValue: forKey获取和设置私有属性;
            dispatch_once(&onceToken, ^{
                statusBar = [[UIView alloc] initWithFrame:[UIApplication sharedApplication].statusBarFrame];
                [[UIApplication sharedApplication].keyWindow addSubview:statusBar];
            });
                    
         } else {
             statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
        }
        if ([statusBar respondsToSelector:@selector(setBackgroundColor:)]) {
            statusBar.backgroundColor = color;
        }
    }
    
    
    

    2.5)iOS13 适配【present 半屏问题】: 如果没适配会导致列表下拉刷新失效

    modalPresentationStyle属性默认不是UIModalPresentationFullScreen了,需要根据需求手动设置

    //
    //  UIViewController+ERPPresent13.m
    //  retail
    //
    //  Created by mac on 2020/2/24.
    //  Copyright © 2020 QCT. All rights reserved.
    //
    
    #import "UIViewController+ERPPresent13.h"
    
    static const char *K_automaticallySetModalPresentationStyleKey;
    
    @implementation UIViewController (ERPPresent13)
    + (void)load {
        Method originAddObserverMethod = class_getInstanceMethod(self, @selector(presentViewController:animated:completion:));
        Method swizzledAddObserverMethod = class_getInstanceMethod(self, @selector(K_presentViewController:animated:completion:));
        method_exchangeImplementations(originAddObserverMethod, swizzledAddObserverMethod);
    }
    
    - (void)setK_automaticallySetModalPresentationStyle:(BOOL)K_automaticallySetModalPresentationStyle {
        objc_setAssociatedObject(self, K_automaticallySetModalPresentationStyleKey, @(K_automaticallySetModalPresentationStyle), OBJC_ASSOCIATION_ASSIGN);
    }
    
    - (BOOL)K_automaticallySetModalPresentationStyle {
        id obj = objc_getAssociatedObject(self, K_automaticallySetModalPresentationStyleKey);
        if (obj) {
            return [obj boolValue];
        }
        return [self.class K_automaticallySetModalPresentationStyle];
    }
    
    + (BOOL)K_automaticallySetModalPresentationStyle {
        if ([self isKindOfClass:[UIImagePickerController class]] || [self isKindOfClass:[UIAlertController class]]) {
            return NO;
        }
        return YES;
    }
    
    - (void)K_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
        if (@available(iOS 13.0, *)) {
            if (viewControllerToPresent.K_automaticallySetModalPresentationStyle) {
                viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
            }
            [self K_presentViewController:viewControllerToPresent animated:flag completion:completion];
        } else {
            // Fallback on earlier versions
            [self K_presentViewController:viewControllerToPresent animated:flag completion:completion];
        }
    }
    
    @end
    
    
    
    
    

    2.6 CNCopyCurrentNetworkInfo:获取WiFi SSID的接口CNCopyCurrentNetworkInfo 不再返回SSID的值,除非先获取用户位置权限才能返回SSID

    - (NSString*) getWifiSsid {
        if (@available(iOS 13.0, *)) {
            //用户明确拒绝,可以弹窗提示用户到设置中手动打开权限
            if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
                NSLog(@"User has explicitly denied authorization for this application, or location services are disabled in Settings.");
                //使用下面接口可以打开当前应用的设置页面
                //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                return nil;
            }
            CLLocationManager* cllocation = [[CLLocationManager alloc] init];
            if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
                //弹框提示用户是否开启位置权限
                [cllocation requestWhenInUseAuthorization];
                usleep(50);
                //递归等待用户选选择
                return [self getWifiSsidWithCallback:callback];
            }
        }
        NSString *wifiName = nil;
        CFArrayRef wifiInterfaces = CNCopySupportedInterfaces();
        if (!wifiInterfaces) {
            return nil;
        }
        NSArray *interfaces = (__bridge NSArray *)wifiInterfaces;
        for (NSString *interfaceName in interfaces) {
            CFDictionaryRef dictRef = CNCopyCurrentNetworkInfo((__bridge CFStringRef)(interfaceName));
    
            if (dictRef) {
                NSDictionary *networkInfo = (__bridge NSDictionary *)dictRef;
                NSLog(@"network info -> %@", networkInfo);
                wifiName = [networkInfo objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID];
                CFRelease(dictRef);
            }
        }
        CFRelease(wifiInterfaces);
        return wifiName;
    }
    
    
    

    III、 iOS13生命周期的改动

    原本AppDelegate(UIApplicationDelegate)控制生命周期的行为移交给了SceneDelegate(UIWindowSceneDelegate)

    如果不使用iPad的多窗口的话建议大家不要使用场景。

    3.1 不使用场景的具体步骤如下:

    1、 使用Xcode11以上新建的项目,删除info.plist UIApplicationSceneManifest
    2、在Appdelegate.h中添加一个window属性
    3、在AppDelegate的didFinishLaunchingWithOptions创建视图

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        
        UITabBarController *tabbarVC = [[UITabBarController alloc] init];
        
        tabbarVC.viewControllers = @[
            [[UINavigationController alloc] initWithRootViewController:[ViewController1 new]],
            [[UINavigationController alloc] initWithRootViewController:[KNViewController2 new]],
            [[UINavigationController alloc] initWithRootViewController:[KNViewController3 new]],
        ];
        
        self.window.rootViewController = tabbarVC;
        [self.window makeKeyAndVisible];
    
        
        
        return YES;
    }
    
    
    

    不使用场景

    see also

    • 补充说明iOS13的UIModalPresentationFullScreen
    //            viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
                
                            viewControllerToPresent.modalPresentationStyle = UIModalPresentationAutomatic;// 1、后果:被怼了很惨,因为项目里有很多模态出来的VC是半透明,结果现在变成完全不透明,背景为黑色。2、 修复拜访记录日期控件PGDatePicker蒙版的问题:点击时间输入框,弹窗蒙版变成了空白
    
                //            2、后遗症:因为把显示效果修改为:UIModalPresentationOverFullScreen;半透明,全屏覆盖的问题都得到完美解决。但是会引发一个新的问题:前一个页面的viewWillAppear:、viewDidAppear:也无法触发。例如: A   Present  B, 有时因为业务逻辑需要,必须在viewWillAppear, viewDidAppear里写一些代码,当B 调用dismiss方法的时候, A的这个两个方法不会触发,因此会有一些安全隐患。因此如果要求B 调用dismiss方法,A要执行viewWillAppear:、viewDidAppear:这个两个方法,这个时候要把B的modalPresentationStyle设置为:UIModalPresentationFullScreen;
    
                            
                //      3、      其他:如果要求 B既要半透明,dismiss时,A还要调用viewWillAppear:、viewDidAppear:。我的想法是在B 写一个block,在B调用dismiss之前,利用block回调A相关的业务逻辑代码。如果有其他更好的方法请告诉我。万分感谢!!
                            
    
    
    展开全文
  • ios系统版本的适配

    2014-11-21 23:09:15
    最近一直在做app的iOS 6和7的同时适配工作,所以在此介绍一下系统与设备的兼容问题。 一、Base SDK与Deployment Target 一个项目(Project)可以有多个目标(Target),每个目标可以指定不同的设置(例如支持...
  • iPhone屏幕尺寸、分辨率及适配

    万次阅读 多人点赞 2017-07-29 12:08:57
    从初代iPhone3GS到现如今的iPhone6(+),屏幕尺寸、分辨率、像素密度都在在不断增大。如何适配不同的屏幕尺寸,...本文结合个人在iOS UI开发和适配方面的粗浅经验,对常用屏幕适配相关因素做个梳理盘点,以备日后查阅。
  • 最新iOS适配-转自简书

    千次阅读 2019-03-05 11:58:16
    iOS 12系统目前看来并没有多大变化.不像iOS 11那样增加了新的安全区域概念.在iOS 12中新机型的安全区域概念没有太大变化. 主要就是机型的适配和Xcode10升级后的问题. 首先附上iOS 12支持的所有机型的尺寸. 适配...
  • ios适配iPhone和iPad

    千次阅读 2019-03-26 15:15:08
    ios适配iPhone和iPad Pinkney 关注 0.1 2017.11.14 17:27 字数 423 阅读 4211评论 3喜欢 7 最近项目需要兼容iPad,由于项目使用xib和Storyboard,故记录对iPhone和iPad的适配! iPhone和ipad适配分为两种种: ...
  • 三分钟搞定安卓+iOS适配问题

    千次阅读 2016-08-11 00:00:27
    如何适配Android手机屏幕 Android适配是一个大坑,你可能早有耳闻。但是别人告诉你坑,然后你也说坑,肯定是无法令人信服的。我们做学问,不能光知其然不知所以然,适配问题到底有多坑,为什么坑,以及如何...
  • 最近比较多的微信小程序项目,写下一篇记录一下安卓正常却在IOS上所出现的问题; 1、时间问题 例:Mysql由中间层取出的时间格式一般为YYYY-MM-DD hh:mm:ss,但是在IOS上无法识别该时间,只能将其转换为YYYY/MM/DD hh:...
  • ios适配

    2018-06-28 12:35:14
    1、input text有圆角在相应界面或css中添加样式:input[type="text"],input[type="number"]{ -webkit-appearance: none; border-radius: 0; }转载自:点击打开链接
  • Android及ios适配原理总结

    千次阅读 2015-07-21 17:42:57
    这里通过对比Android及ios系统解决多机型适配的方法,加深适配问题的理解,如果错误,请指正 对已Android系统来说,由于Android的机型很多,屏幕种类各异,为了在不同的Android设备上保持一致的体验,在开发Android...
  • 怎么适配xsm xr呢, 如果你适配了x那么很简单 如果你用了宏像这样: #define IPHONEX ( ( ([[UIScreen mainScreen] nativeBounds].size.height == 2436) || ([[UIScreen mainScreen] nativeBounds].size.height == ...
  • iOS 适配问题

    2018-08-17 18:12:46
    iOS11之前导航栏默认高度为64pt(这里高度指statusBar + NavigationBar),iOS11之后如果设置了prefersLargeTitles = YES则为96pt,默认情况下还是64pt,但在iPhoneX上由于刘海的出现statusBar由以前的20pt变成了44pt...
  • IOS 适配IOS6

    2014-09-29 00:17:01
    IOS 适配IOS6
  • 声明 此为下策,不到万不得已请谨慎使用,但至少用来处理下燃眉之急没问题,现在是2019年11月27日! 代码中的LQQFinderView是我的View类,代码中的MAIN_SCREEN_H,和MAIN_SCREEN_W定义如下 ...
  • iOS适配不同屏幕下的字体大小

    千次阅读 2017-08-31 15:19:30
    二话不说,上代码,简单粗暴 #import "ViewController.h" ...#pragma mark - 屏幕适配 #define ScreenWidth [UIScreen mainScreen].bounds.size.width #define ScreenHieight [UIScreen mainScree
  • iOS适配的相关内容的整理

    千次阅读 2016-01-05 00:47:54
    iOS适配的相关内容的整理  之前iOS开发者一直很庆幸自己不用像安卓开发者那样适配各种不同类型的机型,但如今随着iPhone各种机型的改变,适配也成了我们开发中必须会的内容了。首先我们来了解一下对于不同苹果设备...
  • 现在,我想把我的适配原则分享给大家,一套游戏资源不改变资源品质下,适配所有IOS设备,你们也可以借鉴一下! 要使用的我的适配原则,第一,你的游戏贴图必须遵守一定规则,都要根据屏幕宽高来确定你的
  • ios 适配

    2017-08-25 13:27:52
    屏幕尺寸适配  2007 年,初代 iPhone发布,其拥有 3.5寸屏幕,运行 iPhone OS X。在后续几年中Apple 陆续发布了 iPhone 3GS,iPhone 4,iPhone 4S,iPhone 5C,iPhone 5S,iPhone 6,iPhone 6 Plus,iPhone 6S...
1 2 3 4 5 ... 20
收藏数 44,264
精华内容 17,705
关键字:

ios适配