iOS自适应视图约束调整

作者: const_zhou | 来源:发表于2016-08-15 13:05 被阅读1539次

    存在的问题


    这种适配问题一直存在于iOS开发中,通常的解决方案有:

    1. 基于代码的布局,通过判断硬件来添加合适约束
    2. 基于xib的布局,给对应的控件的大小和位置添加NSLayoutConstraint outlet变量,通过判断不同的硬件来修改对应的约束。
    3. 实现多套xib文件,根据硬件来加载不同的xib文件
    4. 在xib文件中所有的约束都是基于百分比实现
    5. 只设置一张启动图,不同硬件都基于张图进行拉伸处理

    1、2、3的实现都很繁琐,大量重复性工作。4实现应该是没问题,主要当前视觉给的图基本都是基于像素点的,做的时候需要将像素点转换成对应的百分比,需要做一步计算,很不直观。5的方法会造成在大屏幕上进行了模糊拉伸,显示会比较模糊,影响效果。


    我的解决方案

    原理是:视图的绘制会调用layoutSubviews,我们通过在此方法中修改所有view的constraints,以适配当前的设备。

    寻找合适的约束值(constraints)
    设备 对角线 逻辑分辨率 scale factor 设备分辨率 PPI 高宽比
    4(s) 2.31 inches 4.5 inches 3.5 inch 320x480 @2x 640x960 326 1.5
    5c 2.33 inches 4.9 inches 4 inch 320x568 @2x 640x1136 326 1.775
    5(s) 2.31 inches 4.87 inches 4 inch 320x568 @2x 640x1136 326 1.775
    6 2.64 inches 5.44 inches 4.7 inch 375x667 @2x 750x1334 326 1.779
    6+ 3.06 inches 6.22 inches 5.5 inch 414x736 @3x 1080x1920 401 1.778

    从表中的数据可以得出结论5(s)、6、6+的宽高比基本相同,可以认为只是进行了等比放缩,因此适配的时候只需要对view的尺寸进行对比放缩即可

    通常视觉给的图都是以6的尺寸为模板,我们也以6的尺寸为基本尺寸,则有:

    • 5(s)上view 的尺寸应该是6上尺寸的0.853,也就是6的尺寸是5(s)的1.172倍

    • 6+上view 的尺寸应该是6上尺寸的1.104

    修改约束

    子视图的添加(addSubview)以及frame的修改都会调用layoutSubviews,因此我们可以在layoutSubviews中修改所有的子视图的NSLayoutConstraints的值。

    
    - (void)layoutSubviews
    {
        for (UIView *view in self.subviews) {
            for (int i = 0; i < view.constraints.count; ++ i) {
                NSLayoutConstraint *con = view.constraints[i];
                //获取当前约束值
                CGFloat size = con.constant;
                //修改为合适的约束值
                size = [ZDGAutoSize fitSizeToDevices:size];
                con.constant = size;
            }
        }
    }
    
    
    +(CGFloat) fitSizeToDevices:(CGFloat) length
    {
        //iphone6比iPhone5系列扩大了1.175倍。
        return [self fitSizeToDevices:length withRatio:1.175f];
    }
    +(CGFloat) fitSizeToDevices:(CGFloat) length withRatio:(CGFloat)ratio
    {
        CGFloat fitLength = length;
        if (![self isDeviceAboveIphone5]) {
            fitLength = length / ratio;
        }else if([self isDevicePlus]){
            fitLength = length * 1.104;
        }
        return fitLength;
    }
    
    批量修改约束

    可以看出,所有的修改都在layoutSubviews函数里面。如果对每一个需要自适应的view都重写layoutSubviews方法会非常烦琐,因此我们可以通过runtime方法来完成对layoutSubviews方法的hook,给所有的view添加上我们的修改函数。

    
    //  UIView+AutoSizeToDevice.m
    + (void)load
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Class class = [self class];
            
            SEL originalSelector = @selector(layoutSubviews);
            SEL swizzledSelector = @selector(dg_layoutSubviews);
            
            Method originalMethod = class_getInstanceMethod(class, originalSelector);
            Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
            
            BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
            if (success) {
                class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
            } else {
                method_exchangeImplementations(originalMethod, swizzledMethod);
            }
        });
    }
    
    - (void)dg_layoutSubviews
    {
        // Forward to primary implementation.
        [self dg_layoutSubviews];
        
        if (self.dg_viewAutoSizeToDevice) {
            [self autoSizeToDeviceFromRootView:self];
            self.dg_viewAutoSizeToDevice = NO;
        }
    }
    

    通过hook操作,我们修改了layoutSubviews的实现,完成了所有视图约束的修改。

    项目中的应用

    该方法的源码已上传到github上,下载地址AutoSizeToDevice

    使用方法

    1.在需要自适应的视图上加上头文件

    @import "UIView+AutoSizeToDevice.h"
    

    2.在视图的初始化函数里面设置属性dg_viewAutoSizeToDevice = YES;

    示例

    1.UIViewController中使用

    @import "UIViewController.h"
    @import "UIVew+AutoSizeToDevice.h"
    
    - (void)viewDidLoad()
    {
      [super viewDidLoad];
      self.view.dg_viewAutoSizeToDevice = YES;
    }
    

    2.UIView中使用

    @import "UIView.h"
    @import "UIView+AutoSizeToDevice.h"
    
    - (void)awakeFromNib
    {
      self.dg_viewAutoSizeToDevice = YES;
    }
    
    - (id)initWithFrame:(CGRect)frame
    {
      self = [super initWithFrame:frame];
      if (self){
        self.dg_viewAutoSizeToDevice = YES;
      }
      return self;
    }
    
    

    参考文献:

    1. iPhone屏幕尺寸、分辨率及适配
    2. Autolayout 中的百分比宽度

    相关文章

      网友评论

        本文标题:iOS自适应视图约束调整

        本文链接:https://www.haomeiwen.com/subject/ltsusttx.html