push和present
- 两者的区别
- push:
push由视图栈控制,每一个视图都入栈,调用之前的视图则需要出栈,可返回任意一层,一般用于同一业务不同界面之间的切换。
push是由UINavigationController管理的视图控制器堆栈,在window下同时只能显示一个ViewController。
- present:
present弹出的视图是模态视图(我对模态视图的理解大概就是一个临时视图),只能逐级返回,一般用于不同业务界面的切换。
present是由UIViewController管理的视图控制器堆栈,在window下可以以叠加的方式展示,当顶层的view透明时可以看到底层的view,但只有顶层的view可用户交互。
对push和present的其他补充在我以前的博客中写到过:UI学习之——iOS中界面的推出方法
presentingViewController和presentedViewController
presentedViewController和presentingViewController的区别:
presentedViewController和presentingViewController是UIViewController中的两个属性,例如,如果ViewControllerA调用present(_:animated:completion:)方法将ViewControllerB呈现出来,那么ViewControllerA就是presentingViewController,ViewControllerB就是presentedViewController。
这两个概念在使用Modal视图控制器时非常有用,因为它们允许您跟踪当前视图控制器的状态以及它所呈现的视图控制器。
- presentedViewController:由这个视图控制器或它最近的祖先呈现的视图控制器
- presentingViewController:呈现此视图控制器(或其最远祖先)的视图控制器
dismiss多级的方法:
这里我们使用几个案例来说明:
假设我们有6个视图控制器,颜色和层级关系分别是红->橙->黄->绿->蓝->紫。
dismiss到根视图控制器
按照之前所学的,如果我们直接用dismiss方法,视图会从当前层级跳转到上一层级,那么如果我现在想让它从当前视图控制器直接dismiss到根视图控制器,应该怎么做呢?
给出代码实现:
#import "FiveViewController.h"
#import "FourViewController.h"
@interface FiveViewController ()
@end
@implementation FiveViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor purpleColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"切换" forState:UIControlStateNormal];
button.frame = CGRectMake(100, 300, 200, 80);
[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
- (void)press {
//使用多级dismiss从当前视图返回根视图
//首先创建一个UIViewController的实例对象,用于作为我们的根视图控制器
//presentingViewController属性是指当前视图的上一个视图控制器,我们将刚刚创建的根视图控制器赋为当前视图的上一个视图
UIViewController *rootViewController = self.presentingViewController;
//进入一个循环,一直将该根视图控制器对象赋为它的前一个视图控制器,直到它的前一个视图控制器为nil,则表示我们找到了根视图控制器
while (rootViewController.presentingViewController) {
rootViewController = rootViewController.presentingViewController;
}
//使用dismiss回到根视图控制器
[rootViewController dismissViewControllerAnimated:YES completion:nil];
}
@end
在该代码中,FourViewController是FiveViewController的上一级视图控制器,我们在当前页面先设置一个按钮,并为按钮设置事件函数。该事件函数中我们调用当前视图的presentingViewController属性来获取其上一级视图控制器对象,然后一直循环,直到我们获取到根视图控制器。
在此处,我一开始不理解为什么使用根视图控制器直接dismiss可以回到根视图控制器。因为之前一直以来,我对dismiss的理解都是“销毁当前视图控制器并返回上一级视图控制器”,而这里的rootViewController并不是当前视图控制器,它也没有上一级视图控制器,为什么能这样实现呢?
直到我查到了dismiss的文档描述:
如果您连续显示多个视图控制器,从而构建一个显示的视图控制器堆栈,则在堆栈中较低的视图控制器上调用此方法会解除其直接子视图控制器和堆栈中该子级以上的所有视图控制器。当发生这种情况时,只有最上面的视图以动画的方式被取消;任何中间视图控制器都只是从堆栈中删除。最上面的视图将使用其模态转换样式来消除,这可能与堆栈中较低的其他视图控制器使用的样式不同。
原来是这样,因此,这里不光是根视图控制器,改为其他任何一个视图控制器都是同理
结果演示:
dismiss指定级数
这里演示dismiss两级:
#import "FourViewController.h"
#import "FiveViewController.h"
@interface FourViewController ()
@end
@implementation FourViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"切换" forState:UIControlStateNormal];
button.frame = CGRectMake(100, 300, 200, 80);
[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
UIButton *dismissTwoButton = [UIButton buttonWithType:UIButtonTypeSystem];
[dismissTwoButton setTitle:@"dismiss两级(到黄色视图)" forState:UIControlStateNormal];
dismissTwoButton.frame = CGRectMake(100, 500, 200, 80);
[dismissTwoButton addTarget:self action:@selector(pressDismissTwo) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:dismissTwoButton];
}
- (void)press {
FiveViewController *five = [[FiveViewController alloc] init];
[self presentViewController:five animated:YES completion:nil];
}
- (void) pressDismissTwo {
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
@end
演示结果:
dismiss到目标视图控制器
通过dismiss跳转到目标视图控制器,同样也是要一级一级的通过presentingViewController属性进行遍历,不过这里的循环跳出条件是对便利到的视图控制器的类进行判断
代码实现:
#import "ThreeViewController.h"
#import "FourViewController.h"
#import "OneViewController.h"
@interface ThreeViewController ()
@end
@implementation ThreeViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"切换" forState:UIControlStateNormal];
button.frame = CGRectMake(100, 300, 200, 80);
[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeSystem];
[dismissButton setTitle:@"dismiss到目标控制器(橙色)" forState:UIControlStateNormal];
dismissButton.frame = CGRectMake(100, 500, 200, 80);
[dismissButton addTarget:self action:@selector(pressDismiss) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:dismissButton];
}
- (void)press {
FourViewController *four = [[FourViewController alloc] init];
[self presentViewController:four animated:YES completion:nil];
}
- (void) pressDismiss {
//接下来演示从当前视图控制器dismiss到目标视图控制器
//同样的,我们首先先创建一个视图控制器的实例对象,然后将其赋值为当前视图的前一个视图控制器
UIViewController *muBiaoViewController = self.presentingViewController;
//然后一样的进入while循环,但是此处不同的是出循环的条件是当该对象的父类是目标视图控制器类
while (![muBiaoViewController isKindOfClass:[OneViewController class]]) {
muBiaoViewController = muBiaoViewController.presentingViewController;
}
[muBiaoViewController dismissViewControllerAnimated:YES completion:nil];
}
@end
在该代码中,ThreeViewController是当前视图控制器(绿色),FourViewController是当前视图控制器的上一级视图控制器,OneViewController是我们要跳转的目标视图控制器(橙色)。
演示结果: