当前位置: 首页 > news >正文

【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吗?

相关文章:

【iOS】push和present的区别

【iOS】push和present的区别 文章目录 【iOS】push和present的区别前言pushpop presentdismiss简单小demo来展示dismiss和presentdismiss多级 push和present的区别区别相同点 前言 在iOS开发中&#xff0c;我们经常性的会用到界面的一个切换的问题&#xff0c;这里我们需要理清…...

在Linux服务器上添加用户并设置自动登录

需要在Linux服务器上添加一个新用户&#xff0c;可以使用以下命令 # 这个命令会创建一个新的用户账户&#xff0c;默认情况下不会设置密码&#xff0c;不会在 /home 目录下为新用户创建home目录&#xff1a; # sudo useradd 用户名 # # 如果希望同时为新用户创建家目录&#…...

网站被爬,数据泄露,如何应对不断强化的安全危机?

近年来&#xff0c;众多传统零售商和互联网企业借助大数据、人工智能等先进技术手段&#xff0c;通过场景化设计、优化客户体验、融合线上线下渠道&#xff0c;推动了网络电商行业的消费方式变革&#xff0c;成为电商领域新的增长动力。 但值得注意的是&#xff0c;网络电商带来…...

为什么HTTPS会引入SSL/TLS协议

这时我面试遇到过的问题,整理了一下,希望对大家有帮助! 祝大家秋招顺利! 首先 SSL/TLS 协议通过使用数字证书来实现服务器身份认证, 当用户访问一个 HTTPS 网站时,浏览器会验证服务器的数字证书, 1.首先他对验证整证书是否在有效期 2.其次他会看证书中的服务器域名…...

Spring AOP,通知使用,spring事务管理,spring_web搭建

spring AOP AOP概述 AOP面向切面编程是对面向对象编程的延续&#xff08;AOP &#xff08;Aspect Orient Programming&#xff09;,直译过来就是 面向切面编程,AOP 是一种编程思想&#xff0c;是面向对象编程&#xff08;OOP&#xff09;的一种补充。&#xff09; 面向切面编…...

PHP无缝对接预订无忧场馆预订系统小程序源码

无缝对接&#xff0c;预订无忧 —— 场馆预订系统&#xff0c;让每一次活动都完美启航&#xff01; 一、告别繁琐流程&#xff0c;预订从未如此简单 你是否曾经为了预订一个合适的场馆而焦头烂额&#xff1f;繁琐的咨询、确认、支付流程&#xff0c;让人心力交瘁。但现在&…...

Unet改进30:添加CAA(2024最新改进方法)|上下文锚定注意模块来捕获远程上下文信息。

本文内容:在不同位置添加CAA注意力机制 目录 论文简介 1.步骤一 2.步骤二 3.步骤三 4.步骤四 论文简介 遥感图像中的目标检测经常面临一些日益严峻的挑战,包括目标尺度的巨大变化和不同的测距环境。先前的方法试图通过大核卷积或扩展卷积来扩展主干的空间感受野来解决这…...

OpenAI震撼发布最强模型o1!强化学习突破LLM推理极限

OpenAI新模型无预警上新&#xff1a; o1系列&#xff0c;可以进行通用复杂推理&#xff0c;每次回答要花费更长时间思考。 在解决博士水平的物理问题时&#xff0c;GPT-4o还是“不及格”59.5分&#xff0c;o1一跃来到“优秀档”&#xff0c;直接干到92.8分&#xff01; 没错…...

速通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网关&#xff1a;192.168.1.254 #PC3网关&#xff1a;192.168.3.254 #PC4网关&#xff1a;192.168.4.254# 注&#xff1a;路由器接口必须配置不同网段IP地址 <Huawei>system-view Enter system view, return user view with CtrlZ. #给路由器两个接口配置IP地址 [Hua…...

在 React 中,如何使用 Context API 来实现跨组件的通信?

在 React 中&#xff0c;Context API 提供了一种方式&#xff0c;允许你在组件树中传递数据&#xff0c;而无需在每个层级手动传递 props。这对于实现跨组件通信非常有用&#xff0c;特别是当你需要在多个组件间共享状态或函数时。 以下是如何使用 Context API 来实现跨组件通…...

【基础算法总结】位运算

目录 一&#xff0c;常见位运算操作总结二&#xff0c;算法原理和代码实现191.位1的个数338.比特位计数461.汉明距离面试题01.01.判断字符是否唯一268.丢失的数字371.两整数之和136.只出现一次的数字137.只出现一次的数字II260.只出现一次的数据III面试题17.19.消失的两个数字 …...

组件通信——provide 和 inject 实现爷孙组件通信

provide 和 inject 实现爷孙组件通信 介绍 provide 和 inject 是 Vue.js 提供的一种在组件之间共享数据的机制&#xff0c;它允许在组件树中的任何地方注入依赖项。这对于跨越多个层级的组件间通信特别有用&#xff0c;因此无需手动将 prop 数据逐层传递下去。 provide&#…...

【ShuQiHere】探索人工智能核心:机器学习的奥秘

【ShuQiHere】 &#x1f4a1; 什么是机器学习&#xff1f; 机器学习&#xff08;Machine Learning, ML&#xff09;是人工智能&#xff08;Artificial Intelligence, AI&#xff09;中最关键的组成部分之一。它使得计算机不仅能够处理数据&#xff0c;还能从数据中学习&#x…...

LeeCode打卡第二十四天

LeeCode打卡第二十四天 第一题&#xff1a;对称二叉树&#xff08;LeeCode第101题&#xff09;: 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* …...

什么是科技与艺术相结合的异形创意圆形(饼/盘)LED显示屏

在当今数字化与创意并重的时代&#xff0c;科技与艺术的融合已成为推动社会进步与文化创新的重要力量。其中&#xff0c;晶锐创显异形创意圆形LED显示屏作为这一趋势下的杰出代表&#xff0c;不仅打破了传统显示设备的形态束缚&#xff0c;更以其独特的造型、卓越的显示效果和广…...

AI大模型知识点大梳理_ai大模型知识学习,零基础入门到精通,收藏这一篇就够了

文章目录 AI大模型是什么AI大模型发展历程AI大模型的底层原理AI大模型解决的问题大模型的优点和不足影响个人观点 AI大模型是什么 AI大模型是指具有巨大参数量的深度学习模型&#xff0c;通常包含数十亿甚至数万亿个参数。这些模型可以通过学习大量的数据来提高预测能力&…...

NVG040W语音芯片:为制氧机带来个性化语音提示和报警功能

在当今社会&#xff0c;家庭医疗设备和健康保健产品越来越受到人们的关注。制氧机作为其中的一种&#xff0c;为许多需要氧气治疗的人们提供了重要的帮助。然而&#xff0c;对于许多用户来说&#xff0c;如何正确操作和维护这些设备仍然是一个挑战。为此&#xff0c;NVG040W语音…...

OpenCV结构分析与形状描述符(12)椭圆拟合函数fitEllipseAMS()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆&#xff0c;该椭圆拟合一组2D点。它返回一个内切于该椭圆的旋转矩形。使用了由[260]提出的近…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

Qt的学习(一)

1.什么是Qt Qt特指用来进行桌面应用开发&#xff08;电脑上写的程序&#xff09;涉及到的一套技术Qt无法开发网页前端&#xff0c;也不能开发移动应用。 客户端开发的重要任务&#xff1a;编写和用户交互的界面。一般来说和用户交互的界面&#xff0c;有两种典型风格&…...

JS红宝书笔记 - 3.3 变量

要定义变量&#xff0c;可以使用var操作符&#xff0c;后跟变量名 ES实现变量初始化&#xff0c;因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符&#xff0c;可以创建一个全局变量 如果需要定义…...