iOS中Storyboard的使用

我在开发中一直在使用Storyboard,代码中绝大多数的UIViewController是用Storyboard创建的。在这里分享一些我使用Storyboard的经验。

创建Storyboard

在项目中command+N,弹出如下页面,选中Storyboard然后点击Next给Storyboard命名,创建完成。

图1

进入Storyboard文件,添加一个Scene。

图2

设置Scene对应的类。如下图,在Custom Class填入类名。Identity中Storyboard ID的作用后面会讲。

图3

加载Storyboard

如上图,代码中要获取RDHomeViewController在Storyboard中的这个实例,需要用到UIStoryboard中的API

#import <Foundation/Foundation.h>
#import <UIKit/UIKitDefines.h>

@class UIViewController;

NS_ASSUME_NONNULL_BEGIN

NS_CLASS_AVAILABLE_IOS(5_0) @interface UIStoryboard : NSObject {
}

+ (UIStoryboard *)storyboardWithName:(NSString *)name bundle:(nullable NSBundle *)storyboardBundleOrNil;

- (nullable __kindof UIViewController *)instantiateInitialViewController;
- (__kindof UIViewController *)instantiateViewControllerWithIdentifier:(NSString *)identifier;

@end

NS_ASSUME_NONNULL_END

代码如下

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Home" bundle:nil];

RDHomeViewController *homeViewController = (RDHomeViewController *)[storyboard instantiateViewControllerWithIdentifier:@"RDHomeViewController"];

此处的“Home”为Storyboard创建时的命名,Identifier为图3中的Storyboard ID。

UIStoryboard中的另一个方法

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Home" bundle:nil];

RDHomeViewController *homeViewController = (RDHomeViewController *)[storyboard instantiateInitialViewController];

此方法获取到的是勾选了下图所示项的UIViewController,一个Storyboard中只能有一个Initial View Controller。


图4

Storyboard中添加子视图

在RDHomeViewController中声明一个tableView属性,声明前添加IBOutlet,表示该对象是在Interface Builder中创建的。

@interface RDHomeViewController ()

@property (nonatomic, weak) IBOutlet UITableView *tableView;

@end

然后在Storyboard中添加一个Table View,并和tableView关联。

图5

图5中黑色弹框为选中tableView点击右键弹出。这样就可以在代码中使用在Storyboard中创建的tableView。tableView的许多属性都可以在下图所示位置设置。

图6

Autolayout自动布局

Storyboard中创建的子视图应该使用Autolayout进行布局。自动布局的核心是添加约束。

self.tableView.frame = CGRectMake(100, 100, 100, 100);

这种设置frame的方法使用的是绝对坐标系统,以父视图左上角为原点,每个子视图的x、y都是与原点之前的距离。
Autolayout使用相对坐标系统,通过设置约束确定子视图的位置和尺寸。

图7

上图给tableView添加的约束为,充满屏幕除顶部状态栏和导航栏(如果存在)、底部tabBar(如果存在)之外的部分,无论是什么尺寸的屏幕,tableView都是显示在这部分区域。

图8

通过上图的方式设置约束的constant,此时tableView距离屏幕左边缘50个点,距离屏幕右边缘100个点。

在Storyboard中添加约束可以帮助我们解决大部分屏幕适配的问题,但有些适配需要在代码中完成。比如图8中,在4.7-inch屏幕上,tableView与屏幕左边缘的距离为50,但是在5.5-inch屏幕上,我们希望这个距离为100。此时我们需要在代码中通过判断屏幕尺寸来设置此约束的constant。

@interface RDHomeViewController ()

@property (nonatomic, weak) IBOutlet UITableView *tableView;

@property (nonatomic, weak) IBOutlet NSLayoutConstraint *tableViewLeftConstraint;

@end

每一个约束都是NSLayoutConstraint的实例,我们创建一个IBOutlet 修饰的tableViewLeftConstraint属性,然后与Storyboard中的约束关联。

图9
self.tableViewLeftConstraint.constant = 10;

这样就可以在代码中设置self.tableViewLeftConstraint.constant的值了。

Storyboard中的UITableView

UITableView是我日常开发中使用最多的控件,大部分页面都可以用
tableView实现。下面介绍一下在Storyboard中使用tableView。

在之前的内容中,我们已经在RDHomeViewController中添加一个tableView。接着给tableView添加一个cell,在Storyboard中可以直接把cell拖入tableView。

图10

设置cell对应的类。

RDHomeFirstTableCell是一个继承自UITableViewCell的类。

#import <UIKit/UIKit.h>

@interface RDHomeFirstTableCell : UITableViewCell

@end
图11

设置cell的Identifier。

图12

Identifier是cell的唯一标识,在UITableViewDataSource的- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法中通过Identifier获取到对应的cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIdentifier = @"RDHomeFirstTableCell";
    RDHomeFirstTableCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    return cell;
}

cell添加子视图

在cell上添加一个label,并添加约束。

图13

设置tableView代理

代理可以在代码中设置,也可以在Storyboard中设置。

图14

配置cell子视图

根据面向对象的封装性,cell的子视图应该由cell自己来管理。即外部给cell一个model,cell显示相应的内容。为了更好的体现封装性,除需要在外部使用的属性外,属性声明应该写在匿名类别(即类扩展)中。

@interface RDHomeFirstTableCell ()

@property (nonatomic, weak) IBOutlet UILabel *labelTitle;

@end
图15

将label与labelTitle关联,便可在代码中设置label。按照上述方法再添加一个labelDetail。可以在Storyboard中设置label的text,也可以在代码中设置。

- (void)awakeFromNib {
    [super awakeFromNib];
    self.labelTitle.text = @"我是标题";
    self.labelDetail.text = @"我是详情";
}

在Storyboard中和awakeFromNib方法中设置的是固定的标题,如果需要动态设置,需要添加一个public方法

- (void)configureWithModel:(RDHomeFirstCellModel *)cellModel {
    self.labelTitle.text = cellModel.titleText;
    self.labelDetail.text = cellModel.detailText;
}

在此处调用

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIdentifier = @"RDHomeFirstTableCell";
    RDHomeFirstTableCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    [cell configureWithModel:self.dataSource[indexPath.row]];
    return cell;
}

这样写就是cell自己管理子视图,外部只需要告诉它需要显示的内容,减少了ViewController中的代码量。

如果cell上有action,可以通过delegate回调ViewController。

#import <UIKit/UIKit.h>

@class RDHomeFirstCellModel;
@protocol RDHomeFirstTableCellDelegate;
@interface RDHomeFirstTableCell : UITableViewCell

@property (nonatomic, weak) id <RDHomeFirstTableCellDelegate> delegate;

- (void)configureWithModel:(RDHomeFirstCellModel *)cellModel;

@end

@protocol RDHomeFirstTableCellDelegate <NSObject>

- (void)firstTableCell:(RDHomeFirstTableCell *)firstTableCell didClickButtonWithModel:(RDHomeFirstCellModel *)cellModel;

@end
- (IBAction)buttonClick:(id)sender {
    if ([self.delegate respondsToSelector:@selector(firstTableCell:didClickButtonWithModel:)]) {
        [self.delegate firstTableCell:self didClickButtonWithModel:self.cellModel];
    }
}

cell上添加一个button,关联点击事件。

图16

关于Storyboard合作开发的问题

将Storyboard按模块和功能进行拆分,每个Storyboard只由一个开发者负责,Storyboard之间使用UIStoryboard中的API建立连接,可以尽可能的避免合作开发的问题。

小结

以上就是在日常开发中Storyboard的基本用法,由于笔者的水平有限,如有不足之处,欢迎交流指正。

代码地址:https://github.com/BehindGlory/RDStoryboardDemo

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

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,983评论 3 119
  • 每天接触不同的人,就会发现人与人之间有两种。一种是积极的,一种是消极的,一般情况下我不愿意和消极的打交道,...
    南岸风云阅读 266评论 0 2
  • 我心中的佛 初中的时候,总是被语文老师骂不踏实。甚至有同学在作文里描写我当时的一句口头禅‘‘这道题好简单’’然后老...
    姜能伟阅读 212评论 0 0
  • 喂!你还好吗?我在长沙,而你在哪里?我们每个人都有过擦肩而过,有过失落,也有过一个人的狂欢,我们可以很享受群居带给...
    若然晨曦阅读 166评论 0 1