iOS xib文件根据屏幕等比例缩放的适配

置顶 2018年11月07日 13:23:23 上天眷顾我 阅读数 1001 标签:iOS xib文件根据屏幕等比例缩放的适配  更多

个人分类: iOS开发 

前言

在此我不是和大家讨论,xib相对约束的使用,因为这些文章网上有一大堆的资料,这也不是我今天想要讲的东西。

不知道大家平常有没有碰到过这样的情况。相信很多人在开发中都会使用storyboard和xib来写界面,所见即所得,拖拖拽拽就大工告成了,爽的很。不像纯代码写界面,还要各种alloc、addSubview。可提测后,UI设计师坐在你身边,对UI各种细节调整席卷而来。因为一般设计师是按照4.7屏幕来设计的,这个屏幕下的显示效果没问题,到了小点的屏幕或者大点屏幕的时候,就各种不满意了,各个控件的比例关系并不是他们想要的。每当这个时候就要从xib拉出NSLayoutConstraint属性来动态设置了。还有字体也是,xib设置了,还要钻进代码里面再动态设置一番,真是恶心死了!

所以我在想为什么不能像安卓屏幕适配一样,一切都是等比例适配,我们只要对着一个屏幕尺寸去做开发,其他的自动等比例缩放(按照基准屏幕的宽度去缩放,比如屏幕宽度375的控件width = 10pt,那么屏幕宽度750时就是width = 20pt了)。

解决方案

    下文就是我针对上面的问题,提出的三种解决方案。

第一种:纯代码实现

    为了能够更加好的控制这些UI控件的布局和设置,我开始在新项目中用纯代码去写界面了。虽然用的是Masonnry自动布局,但也难免要设置具体的值,在设值时,我会在加一层AdaptW(floatValue)宏定义包装。

- (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尺寸去换算的。代码如下:

#define kRefereWidth 375.0 // 参考宽度

#define kRefereHeight 667.0 // 参考高度

#define AdaptW(floatValue) [BSFitdpiUtil adaptWidthWithValue:floatValue]

#import<Foundation/Foundation.h>

@interfaceBSFitdpiUtil:NSObject

/**

以屏幕宽度为固定比例关系,来计算对应的值。假设:参考屏幕宽度375,floatV=10;当前屏幕宽度为750时,那么返回的值为20

@param floatV 参考屏幕下的宽度值

@return 当前屏幕对应的宽度值

*/

+ (CGFloat)adaptWidthWithValue:(CGFloat)floatV;

@end

#import"BSFitdpiUtil.h"

@implementationBSFitdpiUtil

+ (CGFloat)adaptWidthWithValue:(CGFloat)floatV;

{

returnfloatV*[[UIScreenmainScreen] bounds].size.width/kRefereWidth;

}

@end

    字体大小的设置,我也是用这种工具类的换算的包装来实现的。

self.bottomLab = [UILabelnew];

[selfaddSubview: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设计师同事还跟我说,新写的界面都要等比例缩放,不然就要各种大小不一的屏幕对一下,我累她也累。

就是因为这种适配的问题,我两年前开始放弃了可视化的布局界面方式,改用纯代码。这次我想保持项目风格的统一,而且也想再次拥抱storyboard和xib,通过查找资料找到利用IBInspectable关键字和分类来实现等比例缩放的功能 ( IBInspectable 就是能够让你的自定义 UIView 的属性出现在 IB 中 Attributes inspector)。具体做法就是:

1.写一个NSLayoutConstraint的分类

2.添加adapterScreen的属性(Bool 值,yes代表需要对屏幕进行等比例适配)

#import<UIKit/UIKit.h>

@interfaceNSLayoutConstraint(BSIBDesignable)

@property(nonatomic,assign) IBInspectableBOOLadapterScreen;

@end

3.在adapterScreen的set方法里面对NSLayoutConstraint对象的constant值进行换算

#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)

@implementationNSLayoutConstraint(BSIBDesignable)

//定义常量 必须是C语言字符串

staticchar*AdapterScreenKey ="AdapterScreenKey";

- (BOOL)adapterScreen{

NSNumber*number = objc_getAssociatedObject(self, AdapterScreenKey);

returnnumber.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,就可以达到想要的等比例适配效果了。


xib等比例.png

    除了给NSLayoutConstraint添加adapterScreen属性,也可以用同样的方式给UILabel、UIButton等对字体大小等比例缩放。但有个很大的缺点就是一个界面有很多控件,每个控件都有Constraints,这个集合里面每个约束都要设置adapterScreen的开关,太麻烦了,而且一旦要对旧的界面也行进同样的操作,想死的心都有。为了解决这个问题,想了个第三种方法。

第三种:用分类去遍历一个view上需要操作的目标并换算

   这个方法其实原理很简单,核心就是一个个遍历换算。代码如下

#import<UIKit/UIKit.h>

typedefNS_ENUM(NSInteger, BSAdaptScreenWidthType) {

AdaptScreenWidthTypeNone =0,

BSAdaptScreenWidthTypeConstraint =1<<0,/**< 对约束的constant等比例 */

BSAdaptScreenWidthTypeFontSize =1<<1,/**< 对字体等比例 */

BSAdaptScreenWidthTypeCornerRadius =1<<2,/**< 对圆角等比例 */

BSAdaptScreenWidthTypeAll =1<<3,/**< 对现有支持的属性等比例 */

};

@interfaceUIView(BSAdaptScreen)

/**

遍历当前view对象的subviews和constraints,对目标进行等比例换算

@param type 想要和基准屏幕等比例换算的属性类型

@param exceptViews 需要对哪些类进行例外

*/

- (void)adaptScreenWidthWithType:(BSAdaptScreenWidthType)type

exceptViews:(NSArray *)exceptViews;

@end

#import"UIView+BSAdaptScreen.h"

// 基准屏幕宽度

#define kRefereWidth 375.0

// 以屏幕宽度为固定比例关系,来计算对应的值。假设:基准屏幕宽度375,floatV=10;当前屏幕宽度为750时,那么返回的值为20

#define AdaptW(floatValue) (floatValue*[[UIScreen mainScreen] bounds].size.width/kRefereWidth)

@implementationUIView(BSAdaptScreen)

- (void)adaptScreenWidthWithType:(BSAdaptScreenWidthType)type

exceptViews:(NSArray *)exceptViews {

if(type == AdaptScreenWidthTypeNone)return;

if(![selfisExceptViewClassWithClassArray:exceptViews]) {


// 是否要对约束进行等比例

BOOLadaptConstraint = ((type & BSAdaptScreenWidthTypeConstraint) || type == BSAdaptScreenWidthTypeAll);


// 是否对字体大小进行等比例

BOOLadaptFontSize = ((type & BSAdaptScreenWidthTypeFontSize) || type == BSAdaptScreenWidthTypeAll);


// 是否对圆角大小进行等比例

BOOLadaptCornerRadius = ((type & BSAdaptScreenWidthTypeCornerRadius) || type == BSAdaptScreenWidthTypeAll);


// 约束

if(adaptConstraint) {

[self.constraints enumerateObjectsUsingBlock:^(__kindofNSLayoutConstraint* _Nonnull subConstraint,NSUIntegeridx,BOOL* _Nonnull stop) {

                subConstraint.constant = AdaptW(subConstraint.constant);

            }];

        }


// 字体大小

if(adaptFontSize) {


if([selfisKindOfClass:[UILabelclass]] && ![selfisKindOfClass:NSClassFromString(@"UIButtonLabel")]) {

UILabel*labelSelf = (UILabel*)self;

labelSelf.font = [UIFontsystemFontOfSize:AdaptW(labelSelf.font.pointSize)];

            }

elseif([selfisKindOfClass:[UITextFieldclass]]) {

UITextField*textFieldSelf = (UITextField*)self;

textFieldSelf.font = [UIFontsystemFontOfSize:AdaptW(textFieldSelf.font.pointSize)];

            }

elseif([selfisKindOfClass:[UIButtonclass]]) {

UIButton*buttonSelf = (UIButton*)self;

buttonSelf.titleLabel.font = [UIFontsystemFontOfSize:AdaptW(buttonSelf.titleLabel.font.pointSize)];

            }

elseif([selfisKindOfClass:[UITextViewclass]]) {

UITextView*textViewSelf = (UITextView*)self;

textViewSelf.font = [UIFontsystemFontOfSize:AdaptW(textViewSelf.font.pointSize)];

            }

        }


// 圆角

if(adaptCornerRadius) {

if(self.layer.cornerRadius) {

self.layer.cornerRadius = AdaptW(self.layer.cornerRadius);

            }

        }


[self.subviews enumerateObjectsUsingBlock:^(__kindofUIView* _Nonnull subView,NSUIntegeridx,BOOL* _Nonnull stop) {

// 继续对子view操作

            [subView adaptScreenWidthWithType:type exceptViews:exceptViews];

        }];

    }

}

// 当前view对象是否是例外的视图

- (BOOL)isExceptViewClassWithClassArray:(NSArray *)classArray {

__blockBOOLisExcept =NO;

[classArray enumerateObjectsUsingBlock:^(Class  _Nonnull obj,NSUIntegeridx,BOOL* _Nonnull stop) {

if([selfisKindOfClass:obj]) {

isExcept =YES;

*stop =YES;

        }

    }];

returnisExcept;

}

@end

最后,不管是用xib拖控件拉约束,还是用纯代码的形式写界面,只要在代码里对父视图调个方法就可以对其本身和子视图,进行约束和字体大小等比例换算了。例如对某个viewcontroller上所有的view进行等比例换算的布局

- (void)viewDidLoad {

[superviewDidLoad];

// Do any additional setup after loading the view.

[selfsetup];

[self.view adaptScreenWidthWithType:BSAdaptScreenWidthTypeAll exceptViews:nil];

}

另外我写了个小小的demo: https://github.com/LvBisheng/TestScaleWithScreen。 我只是提供了一个思路,大家可以根据需要自行对分类进行更改。

总结

   这个问题其实之前困扰我蛮久的,每次想解决,可搜了下网上相关的资料和讨论很少。有时觉得是不是这个等比例换算的需求,本身就需要再斟酌斟酌?还是说大家都有更好更方便的解决方案了......

作者:小生不怕

链接:https://www.jianshu.com/p/cf049bebdc6c

來源:简书

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

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

推荐阅读更多精彩内容