前言
             
            
               
               在此我不是和大家讨论,xib相对约束的使用,因为这些文章网上有一大堆的资料,这也不是我今天想要讲的东西。
            
              解决方案
             
                下文就是我针对上面的问题,提出的三种解决方案。
            
              第一种:纯代码实现
             
            
                 
              为了能够更加好的控制这些UI控件的布局和设置,我开始在新项目中用纯代码去写界面了。虽然用的是Masonnry自动布局,但也难免要设置具体的值,在设值时,我会在加一层AdaptW(floatValue)宏定义包装。
            
            
              
                
                  
                    1 2 3 4 5 6 7 8 9 10 11 12 13  
                  
                    - (void)private_addConstraintForSubViews {      [self.titleView mas_makeConstraints:^(MASConstraintMaker *make) {         make.height.mas_equalTo(AdaptW(55));         make.left.right.equalTo(self);         make.top.equalTo(self);     }];          [self.pageCtl mas_makeConstraints:^(MASConstraintMaker *make) {         make.left.bottom.right.equalTo(self);         make.height.mas_equalTo(AdaptW(8));     }]; }  
                 
              
             
            
               
               AdaptW(floatValue)其实就是一个BSFitdpiUtil工具类方法的调用,以常用的基准屏幕,iphone
              6 的375x667尺寸去换算的。代码如下:
            
            
              
                
                  
                    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  
                  
                    #define kRefereWidth 375.0 // 参考宽度 #define kRefereHeight 667.0 // 参考高度 #define AdaptW(floatValue) [BSFitdpiUtil adaptWidthWithValue:floatValue] #import <Foundation/Foundation.h> @interface BSFitdpiUtil : NSObject /**  以屏幕宽度为固定比例关系,来计算对应的值。假设:参考屏幕宽度375,floatV=10;当前屏幕宽度为750时,那么返回的值为20  @param floatV 参考屏幕下的宽度值  @return 当前屏幕对应的宽度值  */ + (CGFloat)adaptWidthWithValue:(CGFloat)floatV; @end  
                 
              
             
            
              
                
                  
                    1 2 3 4 5 6 7 8 9  
                  
                    #import "BSFitdpiUtil.h" @implementation BSFitdpiUtil + (CGFloat)adaptWidthWithValue:(CGFloat)floatV; {     return floatV*[[UIScreen mainScreen] bounds].size.width/kRefereWidth; } @end  
                 
              
             
            
                 
              字体大小的设置,我也是用这种工具类的换算的包装来实现的。
            
            
              
                
                  
                    1 2 3 4 5  
                  
                    self.bottomLab = [UILabel new]; [self addSubview:self.bottomLab]; self.bottomLab.font = kDefaultFont(Adapt(15)); self.bottomLab.textColor = kFirstTextColor; self.bottomLab.textAlignment = NSTextAlignmentCenter;  
                 
              
             
            
                 
              从此我再也不怕UI设计师来对UI细节了,你要等比例我就等比例给你看,不需要我就在BSFitdpiUtil工具类的adaptWidthWithValue方法,return一个原始值floatV。
            
            
              第二种:利用IBInspectable关键字和分类
             
            
               
               后来我到了新公司接手了个旧项目,工程里几乎所有的界面都是用xib来写的。惨了,UI设计师同事还跟我说,新写的界面都要等比例缩放,不然就要各种大小不一的屏幕对一下,我累她也累。
            
              1.写一个NSLayoutConstraint的分类
            
              
                
                  
                    1 2 3 4 5 6 7  
                  
                     #import <UIKit/UIKit.h> @interface NSLayoutConstraint (BSIBDesignable) @property(nonatomic, assign) IBInspectable BOOL adapterScreen; @end  
                 
              
             
            
              3.在adapterScreen的set方法里面对NSLayoutConstraint对象的constant值进行换算
            
            
              
                
                  
                    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30  
                  
                    #import "NSLayoutConstraint+BSIBDesignable.h" #import <objc/runtime.h> // 基准屏幕宽度 #define kRefereWidth 375.0 // 以屏幕宽度为固定比例关系,来计算对应的值。假设:基准屏幕宽度375,floatV=10;当前屏幕宽度为750时,那么返回的值为20 #define AdaptW(floatValue) (floatValue*[[UIScreen mainScreen] bounds].size.width/kRefereWidth) @implementation NSLayoutConstraint (BSIBDesignable) //定义常量 必须是C语言字符串 static char *AdapterScreenKey = "AdapterScreenKey"; - (BOOL)adapterScreen{     NSNumber *number = objc_getAssociatedObject(self, AdapterScreenKey);     return number.boolValue; } - (void)setAdapterScreen:(BOOL)adapterScreen {          NSNumber *number = @(adapterScreen);     objc_setAssociatedObject(self, AdapterScreenKey, number, OBJC_ASSOCIATION_RETAIN_NONATOMIC);          if (adapterScreen){         self.constant = AdaptW(self.constant);     } } @end  
                 
              
             
            
              4.将该分类导入到工程中,就可以看到xib所有的约束有adapterScreen的属性了,切换至on,就可以达到想要的等比例适配效果了。
            
                 
              除了给NSLayoutConstraint添加adapterScreen属性,也可以用同样的方式给UILabel、UIButton等对字体大小等比例缩放。但有个很大的缺点就是一个界面有很多控件,每个控件都有Constraints,这个集合里面每个约束都要设置adapterScreen的开关,太麻烦了,而且一旦要对旧的界面也行进同样的操作,想死的心都有。为了解决这个问题,想了个第三种方法。
            
            
              第三种:用分类去遍历一个view上需要操作的目标并换算
             
            
               
               这个方法其实原理很简单,核心就是一个个遍历换算。代码如下
            
            
              
                
                  
                    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22  
                  
                    #import <UIKit/UIKit.h> typedef NS_ENUM(NSInteger, BSAdaptScreenWidthType) {     AdaptScreenWidthTypeNone = 0,      BSAdaptScreenWidthTypeConstraint = 1<<0, /**< 对约束的constant等比例 */     BSAdaptScreenWidthTypeFontSize = 1<<1, /**< 对字体等比例 */     BSAdaptScreenWidthTypeCornerRadius = 1<<2, /**< 对圆角等比例 */     BSAdaptScreenWidthTypeAll = 1<<3, /**< 对现有支持的属性等比例 */ }; @interface UIView (BSAdaptScreen) /**  遍历当前view对象的subviews和constraints,对目标进行等比例换算    @param type 想要和基准屏幕等比例换算的属性类型  @param exceptViews 需要对哪些类进行例外  */ - (void)adaptScreenWidthWithType:(BSAdaptScreenWidthType)type                      exceptViews:(NSArray<Class> *)exceptViews; @end  
                 
              
             
            
              
                
                  
                    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77  
                  
                    #import "UIView+BSAdaptScreen.h" // 基准屏幕宽度 #define kRefereWidth 375.0 // 以屏幕宽度为固定比例关系,来计算对应的值。假设:基准屏幕宽度375,floatV=10;当前屏幕宽度为750时,那么返回的值为20 #define AdaptW(floatValue) (floatValue*[[UIScreen mainScreen] bounds].size.width/kRefereWidth) @implementation UIView (BSAdaptScreen) - (void)adaptScreenWidthWithType:(BSAdaptScreenWidthType)type                       exceptViews:(NSArray<Class> *)exceptViews {     if (type == AdaptScreenWidthTypeNone)  return;     if (![self isExceptViewClassWithClassArray:exceptViews]) {               // 是否要对约束进行等比例         BOOL adaptConstraint = ((type & BSAdaptScreenWidthTypeConstraint) || type == BSAdaptScreenWidthTypeAll);                  // 是否对字体大小进行等比例         BOOL adaptFontSize = ((type & BSAdaptScreenWidthTypeFontSize) || type == BSAdaptScreenWidthTypeAll);                  // 是否对圆角大小进行等比例         BOOL adaptCornerRadius = ((type & BSAdaptScreenWidthTypeCornerRadius) || type == BSAdaptScreenWidthTypeAll);                  // 约束         if (adaptConstraint) {             [self.constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull subConstraint, NSUInteger idx, BOOL * _Nonnull stop) {                 subConstraint.constant = AdaptW(subConstraint.constant);             }];         }                  // 字体大小         if (adaptFontSize) {                          if ([self isKindOfClass:[UILabel class]] && ![self isKindOfClass:NSClassFromString(@"UIButtonLabel")]) {                 UILabel *labelSelf = (UILabel *)self;                 labelSelf.font = [UIFont systemFontOfSize:AdaptW(labelSelf.font.pointSize)];             }             else if ([self isKindOfClass:[UITextField class]]) {                 UITextField *textFieldSelf = (UITextField *)self;                 textFieldSelf.font = [UIFont systemFontOfSize:AdaptW(textFieldSelf.font.pointSize)];             }             else  if ([self isKindOfClass:[UIButton class]]) {                 UIButton *buttonSelf = (UIButton *)self;                 buttonSelf.titleLabel.font = [UIFont systemFontOfSize:AdaptW(buttonSelf.titleLabel.font.pointSize)];             }             else  if ([self isKindOfClass:[UITextView class]]) {                 UITextView *textViewSelf = (UITextView *)self;                 textViewSelf.font = [UIFont systemFontOfSize:AdaptW(textViewSelf.font.pointSize)];             }         }                  // 圆角         if (adaptCornerRadius) {             if (self.layer.cornerRadius) {                 self.layer.cornerRadius = AdaptW(self.layer.cornerRadius);             }         }                  [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull subView, NSUInteger idx, BOOL * _Nonnull stop) {             // 继续对子view操作             [subView adaptScreenWidthWithType:type exceptViews:exceptViews];         }];     } } // 当前view对象是否是例外的视图 - (BOOL)isExceptViewClassWithClassArray:(NSArray<Class> *)classArray {     __block BOOL isExcept = NO;     [classArray enumerateObjectsUsingBlock:^(Class  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {         if ([self isKindOfClass:obj]) {             isExcept = YES;             *stop = YES;         }     }];     return isExcept; } @end  
                 
              
             
            
              最后,不管是用xib拖控件拉约束,还是用纯代码的形式写界面,只要在代码里对父视图调个方法就可以对其本身和子视图,进行约束和字体大小等比例换算了。例如对某个viewcontroller上所有的view进行等比例换算的布局
            
            
              
                
                  
                    1 2 3 4 5 6  
                  
                    - (void)viewDidLoad {     [super viewDidLoad];     // Do any additional setup after loading the view.     [self setup];     [self.view adaptScreenWidthWithType:BSAdaptScreenWidthTypeAll exceptViews:nil]; }  
                 
              
             
            
              另外我写了个小小的demo:
              https://github.com/LvBisheng/TestScaleWithScreen。 
              我只是提供了一个思路,大家可以根据需要自行对分类进行更改。
            
            
              PS: 由于现在笔者用的是Swift开发,写了个Swift
              Demo,并支持cocopod导入(https://github.com/LvBisheng/BSAdaptScreen-swift 
            
              首发于简书(https://www.jianshu.com/p/cf049bebdc6c )。
            
            
              总结
             
            
               
               这个问题其实之前困扰我蛮久的,每次想解决,可搜了下网上相关的资料和讨论很少。有时觉得是不是这个等比例换算的需求,本身就需要再斟酌斟酌?还是说大家都有更好更方便的解决方案了……