在Android中,Activity指代一个界面,要跳转到某个指定的界面,方法就这么俩个:startActivity和startActivityForResult,参数可以通过Intent来传送。

但是,在iOS中!这个界面跳转方法真的是非常多样化,传参的方式也是五花八门。如果各种方法混用的话,简直要让人抓狂。故在此进行整理并mark一下

①:两个界面都在一个storyboard上,且已经进行连线

举个例子,如图:

我们在storyboard上面,对着prototype cell连线到另外一个view controller,表示这个tableview的cell点击事件是跳转到另外一个view controller,代码中就不需要写tableview的点击事件了。

如果需要传参数的话,需要给这根线设置identifier:

然后重写一个prepareForSegue方法对目标view controller传参(此处采用属性的方式来传参,省时省力,当然也可以换成其他传参方法,但一定要在这个方法里面处理):

这里只要处理传参,不要关心跳转的问题,因为跳转的部分storyboard已经帮你处理了。

②:同样两个界面都在storyboard上面,但是没有进行连线

通常出现这种情况是因为两个界面在不同的storyboard上,或者是因为某些原因,虽然在同一个storyboard上但是没有连线。此时,我们的处理方式要作出一些变化了。

以①中的情况举例,需要重写一个tableview cell的点击事件:

然后需要在storyboard上对目标的view controller设置一个 storyboard ID。这个id要和上面代码中设置的一致,不然就找不到这个view controller

这样就完成了跳转+传参。

注意,按照我上面方式来写的话,传参和跳转的逻辑都已经包含在了点击事件中,所以就没有必要去重写prepareForSegue了,因为prepareForSegue根本就不会被调用。

③:一个界面在storyboard中,另一个在xib上

这种情况处理起来最简单,直接在点击事件中,把目标view controller new出来,设置属性(传参)然后调用

[self.navigationController pushViewController:XXX animated:YES];

就能进行跳转

④:两个都是xib

同③的处理方法

⑤:手动跳转

这种情况下是将两个view controller连线并设置id,在代码中手动控制跳转。

跳转代码:

[self performSegueWithIdentifier:@"XXXXXX" sender:nil];

传参的话,也要通过prepareForSegue进行。

⑥:纯代码方式,没有xib,没有storyboard:【待补充】

—————————————————————————————————————————————-

总结一下,跳转的代码

在storyboard上寻找指定的view controller:

UIStoryboard *story = [UIStoryboard storyboardWithName:@”MyStoryboardName“bundle:[NSBundle mainBundle]];
MyViewController *storVC = [story instantiateViewControllerWithIdentifier:@”MyViewController “];

通过storyboard进行跳转界面的传参,需要在prepareForSegue中进行参数设置

手动跳转:performSegueWithIdentifier

在上篇文章中,讲了如何用代码来加载xib到app中,这篇讲一下如何在Storyboard上进行调用

首先在Storyboard上建立一个view controller,然后从右侧拖一个view到界面上来

为了偷个懒,就沿用上篇文章中的Test4.xib和Test4ViewController

一、当Test4.xib的file’s owner设置为了Test4

这里需要对Test4进行一些改造,如图:

添加了两个方法,initWithCoder是用于storyboard加载view时候会调用的,这个是目前的关键(注意那个owner是self),上面一个initWithFrame是代码加载view时候调用的,storyboard会视而不见;

再移除Test4ViewController中viewDidLoad中的自定义代码后,运行:

二、当对Test4设置view的custom class时:

【我还没见到这种做法的,暂时就不考虑了】

 

先介绍一种最简单的调用方式

方法一:直接引用

假设我制作了一个名为Test3.xib的xib,然后随便拖了几个控件,如图

创建一个view controller,假设就叫Test3ViewController,在storyboard上拖一个view controller出来,设置Custom class为Test3ViewController

然后在Test3ViewController里面调用它,只是让它显示出来,不改变任何属性:(注意高亮的地方,后面会讲原理)

运行结果:

look,加载上去了。这种加载方式的优点是。。超级简单。缺点是由于布局被写死,开发中基本上不会这么去用

接下来讲一个稍微高级一点的

方法二:属性可变

同样拿上面的Test3.xib来实验,现在对这个xib设置一个files’s owner为Test3ViewController,如图

为了方便看效果,在storyboard上面添加一个按钮,连接action

会自动在m文件里面生成这么个方法,先放着不管

打开Test3.xib,切换到分屏,把两个label连线到Test3ViewController.h上面,命名为xibLabel1和xibLabel2,如图

现在在Test3ViewController.m里面的changeLabel这个按钮事件里添加两行代码(改变xib里面的两个label文字):

运行~咦报错了

这里要注意,因为添加了IBOutlet,所以需要将之前加载xib方法改一参数,看圈里面的

把之前的nil改成self就可以了。原因在于,xib设置了file’s owner,而同时有IBOutlet,所以,这里也要改成和xib设置的相同,此处是self,不然就报错

修改之后再次运行:

点击前的效果👇

点击之后的效果👇

现在来更加炫酷一点的

方法三:自定义class(姑且这么称呼)

这里还有三种办法来达到效果,但是主要是前面两种方案

① XIB 中的 UIView 控件与类关联

为了和上面的作区分,我新建一个xib叫Test4,同时新建Test4.h和Test4.m以及新的view controller

接下来是关键一步。这里不是设置file’s owner,而是设置根view的custom class

添加一个label,连接IBOutlet到Test4上

然后view controller中这么调用

我们来运行一下:

OK,目标达成!

日常使用可以对Test4进行相应的封装,举个例子,我在Test4.h中定义一个初始化方法:

那么,view controller中就调用自己定义的方法:

解释:

这种方式会通过 initWithCoder: 方法来从 XIB 文件初始化该 view。使用的时候我们可以通过 awakeFromNib 方法来做进一步处理。

通过这种方式需要注意的是在initWithCoder:方法中,并没有建立 XIB 和 TestView 的 IBOutlet 连接,所以在这里通过代码引用 XIB 中的控件是引用不到的,而当awakeFromNib执行的时候,各种 IBOutlet 都连接好了。所以如果有子控件的初始化工作,最好放在awakeFromNib里面。

② 通过 File’s owner 来关联

如图:

拖一个IBOutlet到Test4上面。

注意,这种方式创建 view 需要手动来初始化,所以Test4.m中的代码要这么写:

自己定义了一个加载方法为

- (instancetype)initFromNibWithFrame:(CGRect)frame

还添加了一个加载其他设置的方法,初始化可以在这里面进行,这个时候 XIB 和 Test4 的 IBOutlet 连接也已经建立好了.👇

-(void) nib_viewDidLoad

Test4ViewController中的调用是这样的:

CGRectMake中我随便写了个大小,运行:

补充:也可以直接在initWithFrame中进行加载,这样可以省去自定义方法,直接使用 alloc init即可。

(以上这两种方法参考自 独奏 非常感谢这位哥们给我提供了怎么做的思路)

BTW:这位哥们提到了有缺点,我认为如果owner直接写self,即自定义的class的话,②号方法依然是有效的。

③ 邪门歪道法(由于这个是我刚开始的时候自己想出来的,我非常不推荐这么做

在上面的基础上,我们新建一个叫Test3的class

删除之前的IBOutlet,同时把Test3ViewController里面的属性也删除(这里不上图了)

然后把Test3.xib的file’s owner改为Test3,把两个label连接到Test3上

现在,修改Test3ViewController,如下所示:

解释:这里使用了import把Test3引入,创建了Test3的一个对象,并且把这个对象传入到owner这里,同时把xib里面的UILabel的作用域扩展到全局,在按钮点击事件中修改label的文字

运行结果依然是可以达到正确的效果,我就不上图了。

到这里,你已经向自定义控件迈出第一步了 :)


加载xib的核心部分就是这么个方法:

NSArray *views =  [[NSBundle mainBundle] loadNibNamed:@”MyXIB” owner:nil options:nil];

返回值,是xib里面根view的数组。什么叫根view,举个例子,看图

所谓的根view,就是图上箭头所指的。一个xib文件可以包含不止一个根view(我也是后来才发现的)。所以,返回的数组就包含了xib所有的根view。要加载哪个view,取决于你的需求了。

所以,不要迷信网上的写法,比方说这样

AAView *aaView = [[[NSBundle mainBundle] loadNibNamed:@”AAView” owner:self options:nil] lastObject];

比方说,这样

UIView *view = [[[NSBundle mainBundle]loadNibNamed:@”CustomizedView” owner:self options:nil]objectAtIndex:0];

然后来解释一下这个方法的三个参数

第一个参数,name,很简单,就是xib的文件名,不带后缀的

第二个参数,owner,这个是什么呢?看图👇(原文链接:http://blog.csdn.net/xn4545945/article/details/31786391

这里补充一点,如果并没有在xib中建立IBOutlet或者action的话,这个owner就无所谓了,可以填写nil

第三个参数,options,当xib文件开始时,需要的数据(咱也不懂)


关于如何获取根视图里面的子视图,如果file’s owner是别的class的话,有几种办法:

1. 像我上面讲到的图中的,直接引用 xibClass.xibLabel1

2. 给子视图加上tag,通过tag来找到子视图

代码中这么写:UILabel * targetLabel=[parentView viewWithTag:<你设置的tag>];

如果file’s owner是view controller的话,那就直接用IBOutlet拉属性吧!

1.如何新建xib?

xcode8中新建project默认是建立storyboard来管理界面,这是apple推荐的方式,如果要新建xib的话:

方法一:

选择empty,就可以直接建立一个空的xib

这个xib是空的,可以自己添加根view

方法二:

新建的时候选择这个

这样创建出来的xib默认是带了个根视图view的

以上两个方法是只创建xib,还有一种捆绑式创建的方法

方法三:我称之为捆绑式创建

勾选 Also create XIB file,点击next就好了


创建好xib之后,可以对根view进行一些调整,我喜欢以storyboard为主,辅以xib调用

如何调整,看图

选中了根视图之后,右侧的选项,默认都是Inferred。

第一个选项Size是指大小尺寸,如果设置为Freeform,就可以随意调整大小,默认是屏幕大小

第二个选项是指状态栏,None就是去掉状态栏,否则就是显示状态栏

下面俩,没尝试过