在iOS实际项目开发中, 我们经常要适配不同尺寸的屏幕,如iPhone4s,iPhone5/s,iPhone6/s,iPhone6Plus等. 在代码中创建一个控件如:
UILabel *label = [UILabel alloc] init];
label.frame = CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height);
[self.view addSubView : label];
我相信很多童鞋都是这么写的. 这样写也没有错. 但是当控件一多,设置控件的frame就成了一个难题. 有时候我们只能去估算控件的大小以及相对位置. 这样就有一个不好之处: 当你在一个设备上(如iPhone5s) 调整好了布局,那么当你运行在iPhone4s, 或者iPhone6s或者iPhone6s+ 上面的时候,你会发现原本已经布局好的界面变形了. 这就是由于屏幕尺寸的不同导致的问题.那我们该如何解决这个问题呢? 下面我将娓娓道来:
第一种方式也是我们经常使用的Autolayout(自动布局), 值得注意的是,Autolayout只适用 xib 跟 storyBoard. Autolayout是一种“自动布局”技术,专门用来布局UI界面的.Autolayout自iOS6开始引入,由于Xcode4的不给力,当时并没有得到很大的推广.自iOS7(Xcode5)开始,Autolayout的开发效率得到很大的提升.苹果官方也推荐开发者使用Autolayout来布局UI界面.Autolayout能够很轻松的解决屏幕适配的问题. 它是通过在xib或者storyBoard中设置控件的依赖关系,从而适配.
提到Autolayout,不得不提Autoresizing.在Autolayout以前,有Autoresizing可以做屏幕适配,但局限性较大,只能针对父子关系进行有限调整,如边距固定,尺寸可变,对于兄弟关系的调整无法实现.对于UI比较固定的app,这种方式基本满足.相比之下,Autolayout比Autoresizing强大很多.
还有一个就是Size Classes. Size Classes是iOS8中新增了特性,他是对当前所有iOS设备尺寸的一个抽象. 屏幕的宽和高分别分成三种情况:(Compact,Regular,Any).也就是紧凑,正常和任意. 这样宽和高三三整合,一共九种情况. 针对每一种情况,如果需要的话,我们可以单独在storyboard或xib中设置UIView的自动布局约束,甚至某一个button是否显示都是能轻松实现的. 具体使用方法这里就不做详细说明.
下面所要说的就是今天的重点: 纯代码实现屏幕适配. 在开始说之前,先让我们了解一下iPhone的机型和尺寸的对应关系:
很明显能看出这三种屏幕的尺寸宽高比是差不多的,因此可以在5的基础上,按比例放大来适配6和6Plus的屏幕.
// 在AppDelegate.h中
@property float autoSizeScaleX;
@property float autoSizeScaleY;
// 在AppDelegate.m中
#define ScreenHeight [[UIScreen mainScreen] bounds].size.height
#define ScreenWidth [[UIScreen mainScreen] bounds].size.width
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
if(ScreenHeight > 480){ // 这里以(iPhone4S)为准
myDelegate.autoSizeScaleX = ScreenWidth/320;
myDelegate.autoSizeScaleY = ScreenHeight/568;
}else{
myDelegate.autoSizeScaleX = 1.0;
myDelegate.autoSizeScaleY = 1.0;
}
}
因为iPhone4s屏幕的高度是480, 因此当屏幕尺寸大于iPhone4时, autoSizeScaleX和autoSizeScaleY即为当前屏幕和iPhone5尺寸的宽高比, 比如,
如果是5,autoSizeScaleX=1,autoSizeScaleY=1;
如果是6,autoSizeScaleX=1.171875,autoSizeScaleY=1.17429577;
如果是6Plus,autoSizeScaleX=1.29375,autoSizeScaleY=1.2957;
现在我们获取了比例关系后,先来看一下如何解决代码设置界面时的适配。CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)这个方法使我们常用的设置尺寸的方法,现在我设置了一个类似于这样的方法。在.m文件中
CG_INLINE CGRect
TS_CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
CGRect rect;
rect.origin.x = x * myDelegate.autoSizeScaleX;
rect.origin.y = y * myDelegate.autoSizeScaleY;
rect.size.width = width * myDelegate.autoSizeScaleX;
rect.size.height = height * myDelegate.autoSizeScaleY;
return rect;
}
当我们使用的时候直接这样做
UIImageView *imageview = [[UIImageView alloc] initWithFrame:TS_CGRectMake(100, 100, 50, 50)];
这样我们得出的就是转换后的坐标了. 这样,这个imageview在5,6和6Plus的位置和尺寸比例都是一样的. 妈妈再也不用担心屏幕的适配了.
如果整个项目做完后才开始做适配的话这个方法的优势就体现出来了,面对几十个工程文件,只需自定义并且替换你的CGRectMake方法,再加上storyBoradAutoLay这个方法就瞬间完成大部分甚至全部的适配,如果遇到tableView的或者其他的手动调整一下即可.
当然这个我已经准备设计一个类, 写好了我会第一时间放在我的gitHub上,方便大家的使用.