iOS 关于界面布局的一些思考

引子--两个xib的故事

上周小伙伴因为有事请假了,之后他那边负责的模块,经理反映有点问题,于是让我解决下。问题如下:网络环境不好的情况下,首页标的利率一直显示15,直到客户端请求到后台数据才变成10(数字都是例子,当然还有期限,购买人数等显示问题;对p2p平台来说,用户从开始一直显示15再到10的落差还是很大的,用户体验非常不好),其实问题原因很简单,就是本地给的一个默认显示导致,暂时通过将默认值设置成--来解决。
解决过程:
如上所说,问题很简单,首先找到首页大模块,之后查看相应的vc,看了看代码发现,对于这个首页标,为了适配6p屏(5.5)和6p以外的小屏(3.5,4.0,4.7)字体,小伙伴分别用了两个xib。当然这个时候改默认值,就要同时改两个xib;假如以后要改字体颜色或者大小,也要同时修改两个;或者改变这个xib的样式等其它,也要同时修改两个。对于懒人一个的我,着实感觉这样特别不方便,更不要说一些跟model相关的逻辑判断等,功能是实现了,从长远来看非常不利于维护和扩展。

App首页(下文说的App首页指的都是此图)

假如只是为了适配大小屏幕字体,完全可小屏以6统一适配成一个xib,6p的话可以在相应awakeFromNib文件中做适配调整即可,这样不管是界面样式的修改,还是后期相关model数据逻辑的判断,都要容易维护的多。

对自己编码的要求是尽量做到:易维护、易扩展、可重用、逻辑清晰、代码简洁、命名可读性强、注意注释(// /**/ #pragma mark- 等注释方式的使用)等

基于上面种种考虑,哈哈,主要是自己懒的原因,就开始寻找一个能不能更简洁清晰的方法,来布局这个页面。


正文

一、界面布局常见的几种方式

  1. 纯代码布局--计算frame(特别繁琐,不易维护,现在基本上没人在用);
  2. 纯代码布局--使用Masonry第三方库(适合不是很复杂的界面,挺好用);
  3. storyboard布局(加载特慢,简单项目vc不是很多的情况下,可以考虑使用);
  4. 代码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:


    首页标里面subviews布局暂时忽略

对于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,如下图:

给banner占位视图添加水平方向居中约束

坑三: 给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里面,其它地方设置无效。具体原因,搜索之后一网友是这么说的

scrollview autolayout contentsize已确定 无法滚动问题

待探讨

自定义样式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。哇,你真厉害,这么长竟然看完了,赶紧留颗💕再走吧。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,568评论 25 707
  • 我闭上眼。 我看到一片浓墨一般的海水,天空没有太阳也没有星辰,整个一片都是深色,我以为海与天没有...
    Vidrpe阅读 298评论 0 0
  • 雨后的黄昏,并未潮湿了心情。 黄鹂鸟回家歌唱了,小蜗牛还安 然地在我的手心里漫步。这小脸 蛋竟有几分害羞,和我玩起...
    云中散客人阅读 212评论 1 1
  • 这只是我在学Android过程中对于所学知识的巩固和方便日后查询的学习笔记,能帮助到有需要的和我一样的初学者就更好...
    TakeItEasyJQ阅读 192评论 0 0
  • 动态类型 C,C++和Java是静态编译型语言,Python属于动态类型的语言,类型实在运行过程中自动决定的,而不...
    秦汉邮侠阅读 518评论 0 1