引子--两个xib的故事
上周小伙伴因为有事请假了,之后他那边负责的模块,经理反映有点问题,于是让我解决下。问题如下:网络环境不好的情况下,首页标的利率一直显示15,直到客户端请求到后台数据才变成10(数字都是例子,当然还有期限,购买人数等显示问题;对p2p平台来说,用户从开始一直显示15再到10的落差还是很大的,用户体验非常不好),其实问题原因很简单,就是本地给的一个默认显示导致,暂时通过将默认值设置成--来解决。
解决过程:
如上所说,问题很简单,首先找到首页大模块,之后查看相应的vc,看了看代码发现,对于这个首页标,为了适配6p屏(5.5)和6p以外的小屏(3.5,4.0,4.7)字体,小伙伴分别用了两个xib。当然这个时候改默认值,就要同时改两个xib;假如以后要改字体颜色或者大小,也要同时修改两个;或者改变这个xib的样式等其它,也要同时修改两个。对于懒人一个的我,着实感觉这样特别不方便,更不要说一些跟model相关的逻辑判断等,功能是实现了,从长远来看非常不利于维护和扩展。
假如只是为了适配大小屏幕字体,完全可小屏以6统一适配成一个xib,6p的话可以在相应awakeFromNib文件中做适配调整即可,这样不管是界面样式的修改,还是后期相关model数据逻辑的判断,都要容易维护的多。
对自己编码的要求是尽量做到:易维护、易扩展、可重用、逻辑清晰、代码简洁、命名可读性强、注意注释(// /**/ #pragma mark- 等注释方式的使用)等
基于上面种种考虑,哈哈,主要是自己懒的原因,就开始寻找一个能不能更简洁清晰的方法,来布局这个页面。
正文
一、界面布局常见的几种方式
- 纯代码布局--计算frame(特别繁琐,不易维护,现在基本上没人在用);
- 纯代码布局--使用Masonry第三方库(适合不是很复杂的界面,挺好用);
- storyboard布局(加载特慢,简单项目vc不是很多的情况下,可以考虑使用);
- 代码masonry 适当结合xib等混合布局(本文要讲就是其中一种情况)
二、xib布局
- 对于tableview来说,复杂一点的cell,我们通常采用新创建cell文件同时勾选xib的做法,之后在xib中布局UI,将数据变动的控件跟相应的m文件关联,再通过后台请求的数据解析成model来configcell。tableview加载cell的时候,一可以通过tableview注册的方式,二可以直接在代理里面init的方式。
- 对于普通vc来说,可能就是对界面上某一块区域功能单独拎出来,新建一个xib(包括.h,.m和相应xib文件)。例如App首页,小伙伴的布局就是将首页标这一块单独做成了一个xib文件,其它模块通过代码的方式来加载。主要代码框架如下:
-(void)UI{
self.view.backgroundColor =kColorTableBg;
// 取消自动调整scrollView的位置
self.automaticallyAdjustsScrollViewInsets = NO;
//添加首页 banner
[self addBannerViewWithFrame:CGRectMake(0,0,kScreenWidth,SXViewHight)];
//高度对比前165,后156.200000
//添加合力快报新闻滚动
[self.mainScrollView addSubview: self.newsView];
self.mainScrollView.delegate = self;
[self.newsView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@5);
make.top.equalTo(self.mainScrollView).offset(SXViewHight+10);
make.width.equalTo(@(kScreenWidth - 10));
make.height.equalTo(@45);
}];
//添加新手标界面
if (iPhone6Plus) {
[self.mainScrollView addSubview: self.buyerView6p];
[_buyerView6p mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.newsView);
make.top.equalTo(self.newsView.baseline).offset(10);
make.width.equalTo(@(kScreenWidth - 10));
make.height.equalTo(@352);
}];
// _buyerView6p.frame = CGRectMake(5, CGRectGetMaxY(self.newsView.frame)+10, kScreenWidth - 10, 352);
}
else {
[self.mainScrollView addSubview: self.buyerView];
[_buyerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.newsView);
make.top.equalTo(self.newsView.baseline).offset(10);
make.width.equalTo(@(kScreenWidth - 10));
make.height.equalTo(@302);
}];
//self.buyerView6p.frame = CGRectMake(5, CGRectGetMaxY(self.newsView.frame)+10, kScreenWidth - 10, 302);
}
//添加进一步了解合力贷
[self addTips];
//添加底部三张图图片框
[self addImageViews];
}
三、viewcontroller勾选xib 结合自定义view布局(本文所要写的思考,布局App首页)
- 新建VC时勾选xib文件,之后在xib上搭建主要模块的view,分别是banner滚动(暂时占位),合力快报view,首页标view,下面的进一步了解可以相应布局出来(这些都是静态的,不涉及到请求后台数据解析成model更新view的问题);
-
先拿首页标模块举个例子。自定义首页标文件(新建HomeBidView文件,包含h和m不勾选xib),将刚才新建的文件跟相应的xib关联,如下图将xib中首页标class 输入HomeBidView:
对于App首页这个页面,这样布局确实很清晰,一目了然。这种情况不是所有的场景下都适合,结合自己的应用业务需求及UI设计,才能做出最好的界面布局方案。
- 这样布局之后,试图将首页标001也就是里面subview关联到相应HomeBidView.h文件或m文件类别里面时,关联不上。
以前也遇到过类似需求场景,也这样做,但也是卡到这里,以为不能这么做,而只能是自定义view的时候同时勾选xib,之后vc里面通过[[[NSBundle mainBundle]loadNibNamed:@"HomeBidView" owner:self options:nil]lastObject]
来加载这个自定义view。(对IBOutlet这个知识点没有过多深入研究导致)
后来发现通过手动在HomeBidView.m类别里面输入IBOutlet属性,之后逆向关联到xib中,如是ok。App首页其它模块view类似这样搭建就好。整个HomeVC代码逻辑如下(统一在vc里拿到后台数据解析成相应的model,再给到对应的view里面进行展示;当然也可以viewmodel的形式,将请求数据,数据解析成model放到viewmodel里来做)
#import "HomeVC.h"
#import "HomeBidView.h"
#define HeightBanner 150
#define HeightNews 50
#define HeightBid 248
#define HeightKnowIcon 20
#define HeightImage 110
#define HeightPadding 10
@interface HomeVC ()
@property (weak, nonatomic) IBOutlet UIScrollView *scrollview;
@property (weak, nonatomic) IBOutlet UIImageView *ivKnow1;
@property (weak, nonatomic) IBOutlet UIImageView *ivKnow2;
@property (weak, nonatomic) IBOutlet UIImageView *ivKnow3;
@end
@implementation HomeVC
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.navigationBar.hidden = YES;
[self setupBanner];
[self setupNews];
[self setupHomeBid];
[self setupKnowHelloan];
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
//代码设置scrollview的contentsize。注意:1,必须在viewDidAppear里面设置,其它地方无效 2,若不设置即使autolayout约束subview contentsize确定,也不会滚动
self.scrollview.contentSize = CGSizeMake(self.view.frame.size.width,
HeightBanner+HeightNews+HeightBid+HeightKnowIcon+HeightImage*3+HeightPadding*8);
}
#pragma mark- 滚动banner
- (void)setupBanner{
//得到后台banner数据,填充banner滚动view
}
#pragma mark- 合力快报
- (void)setupNews{
//得到后台新闻数据,填充 合力快报view
}
#pragma mark- 首页标
- (void)setupHomeBid{
//得到首页标数据,config HomeBidView
}
#pragma mark- 了解合力贷
- (void)setupKnowHelloan{
//点击imageview事件
//1,首先在xib中打开imageview用户交互
//2,添加手势
UITapGestureRecognizer *ges1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(imageClicked:)];
UITapGestureRecognizer *ges2 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(imageClicked:)];
UITapGestureRecognizer *ges3 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(imageClicked:)];
[self.ivKnow1 addGestureRecognizer:ges1];
[self.ivKnow2 addGestureRecognizer:ges2];
[self.ivKnow3 addGestureRecognizer:ges3];
}
- (void)imageClicked:(UIGestureRecognizer *)ges{
if ([ges.view isEqual:self.ivKnow1]) {
NSLog(@"ivKnow1 imageClicked");
}else if ([ges.view isEqual:self.ivKnow2]){
NSLog(@"ivKnow2 imageClicked");
}else{
NSLog(@"ivKnow3 imageClicked");
}
}
@end
HomeBidView.h文件
#import <UIKit/UIKit.h>
@interface HomeBidView : UIView
+(void)configWithHomeBid:(id)model;
@end
HomeBidView.m文件(为了更方便的查找属性,故将属性写在了m文件 匿名类别里面)
#import "HomeBidView.h"
@interface HomeBidView ()
@property (weak, nonatomic) IBOutlet UILabel *lbTitle;
@end
@implementation HomeBidView
- (void)awakeFromNib{
[super awakeFromNib];
//TODO
self.layer.cornerRadius = 5.0;
self.layer.masksToBounds = YES;
self.lbTitle.text = @"合力新手88888";
}
+(void)configWithHomeBid:(id)model{
//设置首页标的数据
}
@end
附UIScollView autolayout遇到的一些坑
坑一:scroll view has ambiguous scrollable content height问题
解决:在确保scrollview相对于superview四周都约束好了的同时,对scrollview里面的每一个subview都要能确定其高度。
坑二:正常布局scollview里面subview,运行之后发现subview距屏幕右边留出一段距离(这个距离并不是约束的10,且运行之前xib未报约束错误)。如下图:
解决:给scrollview里面第一个subview添加水平方向居中约束,就ok,如下图:
坑三: 给scollview里的subview添加约束contentsize已确定,运行之后发现scollview还是无法滚动
解决:问题可能还是xib某个地方的约束没注意到,暂时改用代码手动设置scollview的contentsize。如下代码:
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
//代码设置scrollview的contentsize。注意:1,必须在viewDidAppear里面设置,其它地方无效 2,若不设置即使autolayout约束subview contentsize确定,也不会滚动
self.scrollview.contentSize = CGSizeMake(self.view.frame.size.width,
HeightBanner+HeightNews+HeightBid+HeightKnowIcon+HeightImage*3+HeightPadding*8);
}
需注意:设置要在viewDidAppear里面,其它地方设置无效。具体原因,搜索之后一网友是这么说的
待探讨
自定义样式button加载图片,关于变形失真问题
解决方案:
- 让ui那边个一个宽度长点的图片;
- 用imageview来加载图片(目前是这样干的,但是要加载多个手势,感觉代码很冗余);这里有个小插曲:开始只是init了一个手势,之后分别加到imageview上,准备通过手势上的view来分别作判断。结果是,前面两个imageview手势不起作用,只有最后一个imageview才可点击。所以只能这样分别init,分别加。
#pragma mark- 了解合力贷
- (void)setupKnowHelloan{
//点击imageview事件
//1,首先在xib中打开imageview用户交互
//2,添加手势
UITapGestureRecognizer *ges1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(imageClicked:)];
UITapGestureRecognizer *ges2 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(imageClicked:)];
UITapGestureRecognizer *ges3 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(imageClicked:)];
[self.ivKnow1 addGestureRecognizer:ges1];
[self.ivKnow2 addGestureRecognizer:ges2];
[self.ivKnow3 addGestureRecognizer:ges3];
}
- (void)imageClicked:(UIGestureRecognizer *)ges{
if ([ges.view isEqual:self.ivKnow1]) {
NSLog(@"ivKnow1 imageClicked");
}else if ([ges.view isEqual:self.ivKnow2]){
NSLog(@"ivKnow2 imageClicked");
}else{
NSLog(@"ivKnow3 imageClicked");
}
}
- button加载图片保留原本图。
Just-do-it
有些知识点有些逻辑,貌似自己知道是一回事,用文字敲打出来又是另外一回事。多学习,多尝试,多思考,多总结(^-^)V。哇,你真厉害,这么长竟然看完了,赶紧留颗💕再走吧。