【iOS】push和present的区别

【iOS】push和present的区别

文章目录

  • 【iOS】push和present的区别
    • 前言
    • push
      • pop
    • present
      • dismiss
      • 简单小demo来展示dismiss和present
      • dismiss多级
    • push和present的区别
        • 区别
        • 相同点

前言

在iOS开发中,我们经常性的会用到界面的一个切换的问题,这里我们需要理清有关视图控制器的push和present的相关方法的区别,以及两种切换方式所对应的视图控制器的形式。

push

这里先给出一个push的简单代码。

ViewController4* vc2 = [[ViewController4 alloc] init];
[self.navigationController pushViewController:vc2 animated:YES];

我们通过这两个语句就可以实现一个push出一个视图控制器的效果,这里讲一下push的原理。

pushViewController:animated: 方法用于将新的视图控制器推入导航栈。这意味着新控制器将显示在当前控制器的上方,同时当前控制器仍然在堆栈中。

pop

push方法对应pop,pop有主要分成两个类别:

  • 第一个类别就是返回到上一层
 [self.navigationController popViewControllerAnimated:YES];
  • 第二个类别就是返回到某一层
[self.navigationController popToRootViewControllerAnimated:YES];//这个是返回到根视图
[self.navigationController popToViewController:viewController animated:YES];//返回指定的某一层视图控制器

这里我们返回某一层的视图控制器可以通过这种方式来返回self.navigationController.viewControllers[i]这里的i是你需要的viewController的层级也可以采用for循环通过判断我们的一个view是否符合isKindeOfClass这个方法来找到对应的UIView,来实现返回某一层的ViewController。

这个给出一个样例:


#import "ViewController3.h"
#import "ViewController4.h"
@interface ViewController3 ()@end@implementation ViewController3- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = UIColor.greenColor;UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];button.frame = CGRectMake(200, 300, 100, 100);[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside];[button setTitle:@"切换" forState:UIControlStateNormal];[button setTitleColor:UIColor.blueColor forState:UIControlStateNormal];[self.view addSubview:button];// Do any additional setup after loading the view.
}
-(void)press {ViewController4* vc2 = [[ViewController4 alloc] init];[self.navigationController pushViewController:vc2 animated:YES];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[self.navigationController popToViewController:self.navigationController.viewControllers[0] animated:YES];
}
/*
#pragma mark - Navigation// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {// Get the new view controller using [segue destinationViewController].// Pass the selected object to the new view controller.
}
*/@end
#import "ViewController1.h"
#import "ViewController2.h"
@interface ViewController1 ()@end@implementation ViewController1- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = UIColor.whiteColor;UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];button.frame = CGRectMake(200, 300, 100, 100);[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside];[button setTitle:@"切换" forState:UIControlStateNormal];[button setTitleColor:UIColor.blueColor forState:UIControlStateNormal];[self.view addSubview:button];// Do any additional setup after loading the view.
}
-(void)press {ViewController2* vc2 = [[ViewController2 alloc] init];[self.navigationController pushViewController:vc2 animated:YES];
}
/*
#pragma mark - Navigation// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {// Get the new view controller using [segue destinationViewController].// Pass the selected object to the new view controller.
}
*/@end#import "ViewController2.h"
#import "ViewController3.h"
@interface ViewController2 ()@end@implementation ViewController2- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = UIColor.redColor;UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];button.frame = CGRectMake(200, 300, 100, 100);[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside];[button setTitle:@"切换" forState:UIControlStateNormal];[button setTitleColor:UIColor.blueColor forState:UIControlStateNormal];[self.view addSubview:button];// Do any additional setup after loading the view.
}
-(void)press {ViewController3* vc2 = [[ViewController3 alloc] init];[self.navigationController pushViewController:vc2 animated:YES];
}/*
#pragma mark - Navigation// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {// Get the new view controller using [segue destinationViewController].// Pass the selected object to the new view controller.
}
*/@end

在这里插入图片描述

这里我是将设计第三层视图控制器如果点击就会返回第一层视图控制器。

present

下面先给出有关于present的简单代码:

ViewController3* vc2 = [[ViewController3 alloc] init];
[self presentViewController:vc2 animated:YES completion:nil];

dismiss

present方法对应dismiss

[self dismissViewControllerAnimated:YES completion:nil];

在我们的认知中的dismiss应该是一层一层逐级返回的,正如下面这个小demo一样。

简单小demo来展示dismiss和present


#import "ViewController1.h"
#import "ViewController2.h"
@interface ViewController1 ()@end@implementation ViewController1- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = UIColor.whiteColor;UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];button.frame = CGRectMake(200, 300, 100, 100);[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside];[button setTitle:@"切换" forState:UIControlStateNormal];[button setTitleColor:UIColor.blueColor forState:UIControlStateNormal];[self.view addSubview:button];// Do any additional setup after loading the view.
}
-(void)press {ViewController2* vc2 = [[ViewController2 alloc] init];[self presentViewController:vc2 animated:YES completion:nil];
}
/*
#pragma mark - Navigation// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {// Get the new view controller using [segue destinationViewController].// Pass the selected object to the new view controller.
}
*/@end#import "ViewController2.h"
#import "ViewController3.h"
@interface ViewController2 ()@end@implementation ViewController2- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = UIColor.redColor;UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];button.frame = CGRectMake(200, 300, 100, 100);[button addTarget:self action:@selector(press) forControlEvents:UIControlEventTouchUpInside];[button setTitle:@"切换" forState:UIControlStateNormal];[button setTitleColor:UIColor.blueColor forState:UIControlStateNormal];[self.view addSubview:button];// Do any additional setup after loading the view.
}
-(void)press {ViewController3* vc2 = [[ViewController3 alloc] init];[self presentViewController:vc2 animated:YES completion:nil];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[self dismissViewControllerAnimated:YES completion:nil];
}
/*
#pragma mark - Navigation// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {// Get the new view controller using [segue destinationViewController].// Pass the selected object to the new view controller.
}
*/@end

这里代码部分只展示两个视图控制器,因为四个视图控制器大致相同。

在这里插入图片描述

dismiss多级

但是present还有两个方法可以让我们实现一个跨级返回的效果。presentingViewControllerpresentedViewController这两个方法分别是什么呢?这里简单解释一下返回两个的对应视图控制器。

当从1中弹出2后:

  1. self.presentingViewController 在1中,就是nil;在2中,就是1
  2. self.presentedViewController在1中,就是2;在2中,就是nil

这里先给出一个通过dismisspresentingViewController这两个方法来实现多级返回的例子。

我们在原先的第四个视图控制器中设置多级返回的函数

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {//如果触碰屏幕就实现一个层级返回UIViewController* vc = [self presentingViewController];//设置一个vc来寻找他的viewcontrollerwhile (vc.presentingViewController) {//如果他的前一个视图控制器不是nil就继续寻找,说白了也就是寻找第一个视图控制器vc =  [vc presentingViewController];//不断向前寻找对应的present过的视图控制器。}[vc dismissViewControllerAnimated:YES completion:nil];
}

在这里插入图片描述

可以看到这里最后我们明明处于第一层视图控制器,却可以实现返回第一个视图控制器的效果,这里与我之前对于dismiss的理解有所不同,我一直认为是在后一层的视图控制器可以实现一个返回前一层的效果,但是实际上并非如此,这里我重新学习了一下dismiss这个函数的作用,附一段大佬的博客:你真的了解iOS中控制器的present和dismiss吗?

我们对于dismiss的调用一贯以来都是一个在2视图控制器这个位置调用,但是实际上这个方式是错误的,我们应该返回到他的上一层视图控制器也就是我们原先的1视图控制器:

实际上,dismiss方法系统会自动优化,当B视图控制器调用dismiss时,它并没有打开任何界面,就将dismissViewController方法会自动交给B的presentingViewController执行,也就是A来执行。

同时dismiss的真正意义在苹果官方文档里面是这样写的

如果你连续呈现多个视图控制器,从而构建一个呈现的视图控制器堆栈,那么在堆栈中较低的视图控制器上调用此方法将消除其直接的子视图控制器和堆栈上该子视图上方的所有视图控制器。发生这种情况时,只有最顶层的视图会以动画方式关闭;任何中间视图控制器都只是从堆栈中删除。最顶层的视图使用其模态过渡样式关闭,该样式可能与堆栈中较低的其他视图控制器使用的样式不同。苹果官方文档

所以这里如果我们按照上述的方法的正确的方式就是用第一个视图控制器调用dismiss方法,只有最上层的视图控制器会以动画方式关闭,其他仅仅是从代码层面上进行一个删除,然后就可以实现我们的这里的一个返回根视图控制器。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {UIViewController* vc = [self presentingViewController];vc =  [vc presentingViewController];[vc dismissViewControllerAnimated:YES completion:nil];
}

我们可以选择连续返回两层,只要修改这个函数成下面就可以实现这样的效果。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {UIViewController* vc = [self presentingViewController];vc =  [vc presentingViewController];[vc dismissViewControllerAnimated:YES completion:nil];
}

在这里插入图片描述

我们也可以通过判断视图控制器的类型来返回我们指定的视图控制器

这里我们的目标是返回推出的第二个视图控制器,其实内容差不多,只不过判断我们要返回那一个视图控制器,用判断类的方法来判断我们要返回的类。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {UIViewController* vc = [self presentingViewController];while (![vc isKindOfClass:[ViewController2 class]]) {vc = [vc presentingViewController];}[vc dismissViewControllerAnimated:YES completion:nil];
}

push和present的区别

区别
  • push:
    • push 方法是通过 UINavigationController 进行导航,新的视图控制器会被压入导航栈中,可以跨级返回,push是由UINavigationController管理的视图控制器堆栈,在window下同时只能显示一个ViewController。 push一般用于同一业务不同界面间的切换。
  • present:
    • present 方法是通过模态展示的方式推出新的视图控制器,present是由UIViewController管理的视图控制器堆栈,在window下可以以叠加的方式展示,当顶层的view透明时可以看到底层的view,但只有顶层的view可用户交互,而且只能逐级返回(一般情况)。present一般用于不同业务界面的切换。
相同点
  • 两者都是用于切换界面的,只不过适用的业务逻辑不一样。

笔者这里简单学习了一下有关与push和present的区别,以及present的一些相关内容来实现一个多级跳转的功能,其实有关于视图控制器的内容很多,笔者简单总结了一部分。

参考博客:
苹果官方文档
你真的了解iOS中控制器的present和dismiss吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/879617.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络(四)——HTTP协议

文章目录 认识urlurlencode和urldecodeHTTP协议格式HTTP的方法HTTP的状态码HTTP常见Header 虽然应用层的协议是由人为规定的&#xff0c;但是已经有大佬们定义了一些现成的&#xff0c;又非常好用的应用层协议&#xff0c;供我们直接参考使用. HTTP(超文本传输协议)就是其中之一…

适合骑行的开放式耳机哪个品牌好?四款开放式蓝牙耳机推荐

骑行时是否有必要佩戴耳机是一个需要权衡安全与便利的问题。因为虽然耳机能提供音乐、导航等功能&#xff0c;但也可能分散注意力&#xff0c;影响骑行安全。而且这也是需要看个人需求决定的&#xff0c;骑行戴耳机的需求是什么&#xff0c;我想大部分人应该就是为了接听电话&a…

C++:用类实现链表,队列,栈

用类实现链表&#xff1a; #include<iostream> using namespace std;class List {struct Node{int Nval;Node* pNext;}; private:Node* pHead NULL;Node* pEnd NULL; public:void AddNode(int n);void DelNode(int n);void walk(); }; void List::AddNode(int k) {Nod…

[数据集][目标检测]无人机识别检测数据集VOC+YOLO格式6986张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;6986 标注数量(xml文件个数)&#xff1a;6986 标注数量(txt文件个数)&#xff1a;6986 标注…

MySQL —— 视图

概念 视图是一张虚拟的表&#xff0c;它是基于一个或多个基本表或其他视图的查询结果集。 视图本身不存储数据&#xff0c;而是通过执行查询来动态生成数据&#xff0c;用户可以像操作普通表一样使用视图来进行查询更新与管理等操作。 视图本身也不占用物理存储空间&#xf…

Redis基础数据结构之 Sorted Set 有序集合 源码解读

目录标题 Sorted Set 是什么?Sorted Set 数据结构跳表&#xff08;skiplist&#xff09;跳表节点的结构定义跳表的定义跳表节点查询层数设置 Sorted Set 基本操作 Sorted Set 是什么? 有序集合&#xff08;Sorted Set&#xff09;是 Redis 中一种重要的数据类型&#xff0c;…

SQL Server数据库简单的事务日志备份恢复

模拟数据库备份恢复过程 1.基础操作 1.创建TestDB数据库&#xff0c;并添加数据 USE [master] GO CREATE DATABASE TestDB CONTAINMENT NONE ON PRIMARY ( NAME NTestDB, FILENAME ND:\TestDB.mdf , SIZE 8192KB , MAXSIZE UNLIMITED, FILEGROWTH 65536KB ) LOG ON ( …

【ArcGIS Pro实操第七期】栅格数据合并、裁剪及统计:以全球不透水面积为例

【ArcGIS Pro实操第七期】批量裁剪&#xff1a;以全球不透水面积为例 准备&#xff1a;数据下载ArcGIS Pro批量裁剪数据集1 数据拼接2 数据裁剪3 数据统计&#xff1a;各栅格取值3.1 栅格计算器-精确提取-栅格数据特定值3.2 数据统计 4 不透水面积变化分析 参考 准备&#xff1…

DAY13信息打点-Web 应用源码泄漏开源闭源指纹识别GITSVNDS备份

#知识点 0、Web架构资产-平台指纹识别 1、开源-CMS指纹识别源码获取方式 2、闭源-习惯&配置&特性等获取方式 3、闭源-托管资产平台资源搜索监控 演示案例&#xff1a; ➢后端-开源-指纹识别-源码下载 ➢后端-闭源-配置不当-源码泄漏 ➢后端-方向-资源码云-源码泄漏 …

[C++进阶]AVL树

前面我们说了二叉搜索树在极端条件下时间复杂度为O(n),本篇我们将介绍一种对二叉搜索树进行改进的树——AVL树 一、AVL 树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找效率低下。因此&#xff0c;两位…

人工智能和大模型的简介

文章目录 前言一、大模型简介二、大模型主要功能1、自然语言理解和生成2、文本总结和翻译3、文本分类和信息检索4、多模态处理三、大模型的技术特性1、深度学习架构2、大规模预训练3、自适应能力前言 随着技术的进步,人工智能(Artificial Intelligence, AI)和机器学习(Mac…

【微服务】springboot 整合表达式计算引擎 Aviator 使用详解

目录 一、前言 二、表达式计算框架概述 2.1 规则引擎 2.1.1 什么是规则引擎 2.1.2 规则引擎用途 2.1.3 规则引擎使用场景 2.2 表达式计算框架 2.2.1 表达式计算框架定义 2.2.2 表达式计算框架特点 2.2.3 表达式计算框架应用场景 2.3 表达式计算框架与规则引擎异同点 …

Pytorch详解-Pytorch核心模块

Pytorch核心模块 一、Pytorch模块结构_pycache__Cincludelibautogradnnoptimutils 二、Lib\site-packages\torchvisiondatasetsmodelsopstransforms 三、核心数据结构——Tensor&#xff08;张量&#xff09;在深度学习中&#xff0c;时间序列数据为什么是三维张量&#xff1f;…

Imagen:重塑图像生成领域的革命性突破

目录 引言 一、Imagen模型的技术原理 1. 模型概述 2. 工作流程 3. 技术创新 二、Imagen模型的应用实例 1. 创意设计 2. 虚拟角色制作 3. 概念可视化 三、Imagen模型的优势与挑战 1. 优势 2. 挑战 四、Imagen模型的未来发展方向 1. 图像生成质量的提升 2. 多模态…

SIPp uac.xml 之我见

https://sipp.sourceforge.net/doc/uac.xml.html 这个 uac.xml 有没有问题呢&#xff1f; 有&#xff01; 问题之一是&#xff1a; <recv response"200" rtd"true" rrs"true"> 要加 rrs, 仔细看注释就能看到 问题之二是&#xff1…

vue3补充

form表单重置 const { proxy } getCurrentInstance()!; // 获取挂载在全局的上下文proxy.resetForm(ruleFormRef); // 在el-form中清空ref为ruleFormRef的表单注&#xff1a;不推荐使用 不推荐的原因 类型安全问题&#xff1a; 当在 TypeScript 环境中使用时&#xff0c;…

算法leecode笔记

具体代码&#xff1a; class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> hashtable;for (int i 0; i < nums.size(); i) {auto it hashtable.find(target - nums[i]);if (it ! hashtable.end…

Rust编写Windows服务

文章目录 Rust编写Windows服务一&#xff1a;Windows服务程序大致原理二&#xff1a;Rust中编写windows服务三&#xff1a;具体实例 Rust编写Windows服务 编写Windows服务可选语言很多, 其中C#最简单。本着练手Rust语言&#xff0c;尝试用Rust编写一个服务。 一&#xff1a;Win…

Git之如何删除Untracked文件(六十八)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

【编程基础知识】mysql根据某个int字段计算到每一行为止的累加值怎么实现

一、方式一&#xff1a;窗口函数 在MySQL中&#xff0c;可以使用窗口函数&#xff08;Window Functions&#xff09;来计算每一行的累加值。如果你使用的是MySQL 8.0或更高版本&#xff0c;可以使用 SUM() 窗口函数结合 OVER() 子句来实现这个需求。 假设你有一个名为 sales 的…