【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还有两个方法可以让我们实现一个跨级返回的效果。presentingViewController 和presentedViewController这两个方法分别是什么呢?这里简单解释一下返回两个的对应视图控制器。
当从1中弹出2后:
- self.presentingViewController 在1中,就是nil;在2中,就是1
- self.presentedViewController在1中,就是2;在2中,就是nil
这里先给出一个通过dismiss和presentingViewController这两个方法来实现多级返回的例子。
我们在原先的第四个视图控制器中设置多级返回的函数
-(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吗?
相关文章:
【iOS】push和present的区别
【iOS】push和present的区别 文章目录 【iOS】push和present的区别前言pushpop presentdismiss简单小demo来展示dismiss和presentdismiss多级 push和present的区别区别相同点 前言 在iOS开发中,我们经常性的会用到界面的一个切换的问题,这里我们需要理清…...
在Linux服务器上添加用户并设置自动登录
需要在Linux服务器上添加一个新用户,可以使用以下命令 # 这个命令会创建一个新的用户账户,默认情况下不会设置密码,不会在 /home 目录下为新用户创建home目录: # sudo useradd 用户名 # # 如果希望同时为新用户创建家目录&#…...
网站被爬,数据泄露,如何应对不断强化的安全危机?
近年来,众多传统零售商和互联网企业借助大数据、人工智能等先进技术手段,通过场景化设计、优化客户体验、融合线上线下渠道,推动了网络电商行业的消费方式变革,成为电商领域新的增长动力。 但值得注意的是,网络电商带来…...
为什么HTTPS会引入SSL/TLS协议
这时我面试遇到过的问题,整理了一下,希望对大家有帮助! 祝大家秋招顺利! 首先 SSL/TLS 协议通过使用数字证书来实现服务器身份认证, 当用户访问一个 HTTPS 网站时,浏览器会验证服务器的数字证书, 1.首先他对验证整证书是否在有效期 2.其次他会看证书中的服务器域名…...
Spring AOP,通知使用,spring事务管理,spring_web搭建
spring AOP AOP概述 AOP面向切面编程是对面向对象编程的延续(AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。) 面向切面编…...
PHP无缝对接预订无忧场馆预订系统小程序源码
无缝对接,预订无忧 —— 场馆预订系统,让每一次活动都完美启航! 一、告别繁琐流程,预订从未如此简单 你是否曾经为了预订一个合适的场馆而焦头烂额?繁琐的咨询、确认、支付流程,让人心力交瘁。但现在&…...
Unet改进30:添加CAA(2024最新改进方法)|上下文锚定注意模块来捕获远程上下文信息。
本文内容:在不同位置添加CAA注意力机制 目录 论文简介 1.步骤一 2.步骤二 3.步骤三 4.步骤四 论文简介 遥感图像中的目标检测经常面临一些日益严峻的挑战,包括目标尺度的巨大变化和不同的测距环境。先前的方法试图通过大核卷积或扩展卷积来扩展主干的空间感受野来解决这…...
OpenAI震撼发布最强模型o1!强化学习突破LLM推理极限
OpenAI新模型无预警上新: o1系列,可以进行通用复杂推理,每次回答要花费更长时间思考。 在解决博士水平的物理问题时,GPT-4o还是“不及格”59.5分,o1一跃来到“优秀档”,直接干到92.8分! 没错…...
速通GPT-2:Language Models are Unsupervised Multitask Learners全文解读
文章目录 GPT系列论文速通引言总览GPT和GPT-2区别Abstract1. 概括2. 具体分析 Introduction1. 概括2. 具体分析当前机器学习系统的局限性希望构建通用型系统数据集与任务通用性缺乏的原因 Approach1. 概括与要点2. 原文阅读翻译3. 具体分析论文核心Training DatasetInput Repre…...
Python 最小公倍数计算器:从基础到应用
目录 引言数学背景 什么是最小公倍数(LCM)计算LCM的方法Python基础 Python简介Python安装和设置使用Python计算最小公倍数 理论基础Python实现详细代码解析 辅助函数LCM计算函数最小公倍数的应用 工作中的应用场景日常生活中的应用场景优化与扩展 代码优化处理多个数字进阶话…...
网络学习-eNSP配置路由器
#PC1网关:192.168.1.254 #PC3网关:192.168.3.254 #PC4网关:192.168.4.254# 注:路由器接口必须配置不同网段IP地址 <Huawei>system-view Enter system view, return user view with CtrlZ. #给路由器两个接口配置IP地址 [Hua…...
在 React 中,如何使用 Context API 来实现跨组件的通信?
在 React 中,Context API 提供了一种方式,允许你在组件树中传递数据,而无需在每个层级手动传递 props。这对于实现跨组件通信非常有用,特别是当你需要在多个组件间共享状态或函数时。 以下是如何使用 Context API 来实现跨组件通…...
【基础算法总结】位运算
目录 一,常见位运算操作总结二,算法原理和代码实现191.位1的个数338.比特位计数461.汉明距离面试题01.01.判断字符是否唯一268.丢失的数字371.两整数之和136.只出现一次的数字137.只出现一次的数字II260.只出现一次的数据III面试题17.19.消失的两个数字 …...
组件通信——provide 和 inject 实现爷孙组件通信
provide 和 inject 实现爷孙组件通信 介绍 provide 和 inject 是 Vue.js 提供的一种在组件之间共享数据的机制,它允许在组件树中的任何地方注入依赖项。这对于跨越多个层级的组件间通信特别有用,因此无需手动将 prop 数据逐层传递下去。 provide&#…...
【ShuQiHere】探索人工智能核心:机器学习的奥秘
【ShuQiHere】 💡 什么是机器学习? 机器学习(Machine Learning, ML)是人工智能(Artificial Intelligence, AI)中最关键的组成部分之一。它使得计算机不仅能够处理数据,还能从数据中学习&#x…...
LeeCode打卡第二十四天
LeeCode打卡第二十四天 第一题:对称二叉树(LeeCode第101题): 给你一个二叉树的根节点 root , 检查它是否轴对称。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* …...
什么是科技与艺术相结合的异形创意圆形(饼/盘)LED显示屏
在当今数字化与创意并重的时代,科技与艺术的融合已成为推动社会进步与文化创新的重要力量。其中,晶锐创显异形创意圆形LED显示屏作为这一趋势下的杰出代表,不仅打破了传统显示设备的形态束缚,更以其独特的造型、卓越的显示效果和广…...
AI大模型知识点大梳理_ai大模型知识学习,零基础入门到精通,收藏这一篇就够了
文章目录 AI大模型是什么AI大模型发展历程AI大模型的底层原理AI大模型解决的问题大模型的优点和不足影响个人观点 AI大模型是什么 AI大模型是指具有巨大参数量的深度学习模型,通常包含数十亿甚至数万亿个参数。这些模型可以通过学习大量的数据来提高预测能力&…...
NVG040W语音芯片:为制氧机带来个性化语音提示和报警功能
在当今社会,家庭医疗设备和健康保健产品越来越受到人们的关注。制氧机作为其中的一种,为许多需要氧气治疗的人们提供了重要的帮助。然而,对于许多用户来说,如何正确操作和维护这些设备仍然是一个挑战。为此,NVG040W语音…...
OpenCV结构分析与形状描述符(12)椭圆拟合函数fitEllipseAMS()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆拟合一组2D点。它返回一个内切于该椭圆的旋转矩形。使用了由[260]提出的近…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
二叉树-144.二叉树的前序遍历-力扣(LeetCode)
一、题目解析 对于递归方法的前序遍历十分简单,但对于一位合格的程序猿而言,需要掌握将递归转化为非递归的能力,毕竟递归调用的时候会调用大量的栈帧,存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧,而非…...
