UIViewController
# 07.UIViewController设计
# 目录介绍
- 01.UIViewController
- 1.1 Controller介绍
- 1.2 Controller功能
- 02.Controller主要职责
- 2.1 核心职责说明
- 2.2 核心相关类说明
- 2.3 UINavigationController
- 03.Controller生命周期
- 3.1 初始化方法
- 3.2 加载视图方法
- 3.3 视图出现与消失
- 3.4 视图加载时机
- 3.5 状态变化处理
- 3.6 系统事件响应
- 3.7 代码开发建议
- 04.Controller内置属性
- 4.1 主要的属性
- 4.2 属性设置实践
- 05.Controller导航方法
- 5.1 嵌入与弹出
- 5.2 推送与弹出
- 5.3 携带参数并回调
- 06.视图层次管理构建
- 6.1 子视图添加&移除
- 6.2 视图层次布局与约束
- 6.3 控件添加与布局
- 6.4 数据绑定UI
- 07.手势识别器应用
- 7.1 手势识别器的类型
- 7.2 手势与视图交互
- 08.控制器高级应用
- 8.1 模态展示与解除
- 8.2 视图状态保存与恢复
- 8.3 UINavigationController
- 8.4 视图切换动画示例
# 01.UIViewController
# 1.1 Controller介绍
UIViewController是iOS开发中的一个重要类,用于管理应用程序的用户界面。它是UIKit框架中的一部分,用于创建和管理视图层次结构、处理用户交互以及响应系统事件。
UIViewController负责管理一个屏幕上的视图,并处理与该视图相关的逻辑。它可以包含其他视图控制器,形成视图控制器层次结构。
# 1.2 Controller功能
以下是UIViewController的一些常见功能和用途:
- 视图管理:UIViewController负责加载和管理视图层次结构。它可以创建和配置视图,将视图添加到屏幕上,并处理视图的布局和显示。
- 用户交互:UIViewController可以处理用户的输入和交互。它可以响应按钮点击、手势操作、文本输入等用户事件,并执行相应的操作。
- 数据传递:UIViewController可以接收和传递数据。它可以从其他视图控制器获取数据,并将数据传递给其他视图控制器或模型对象。
- 生命周期管理:UIViewController具有生命周期方法,用于管理视图控制器的创建、加载、显示和销毁过程。通过这些方法,您可以执行适当的初始化、配置和清理操作。
- 导航和转场:UIViewController可以与导航控制器、标签栏控制器等容器控制器一起使用,以实现应用程序的导航和视图切换。
- 状态保存和恢复:UIViewController支持应用程序的状态保存和恢复。它可以保存和恢复视图控制器的状态,以便在应用程序重新启动后恢复到之前的状态。
UIViewController是iOS应用程序开发中的核心概念之一,它提供了丰富的功能和灵活性,用于构建交互式和响应式的用户界面。
# 02.Controller主要职责
# 2.1 核心职责说明
类 UIViewController 是视图控制器层次结构的基类。视图控制器管理 UIView和其他 UIViewController。iOS 应用程序具有单个窗口,但多个屏幕,每个屏幕可能包含多个 UIView。管理这些屏幕很复杂,需要响应用户输入和模型中的更改, (问题域) 。这种管理和协调是 的工作 UIViewController。
UIViewController 有 3 个主要职责:
- 布局出其组件 UIView。 这包括调整大小、响应方向更改等。
- 调整显示结构以响应输入事件或模型类的状态
- 将用户输入转换为与平台无关的模型服务请求
# 2.2 核心相关类说明
以下是一些与UIViewController控制器密切相关的核心类的说明:
- UINavigationController 是一个导航控制器类,用于管理视图控制器的导航堆栈。它提供了导航栏和导航栏控制按钮,以便用户可以在视图控制器之间进行推入(push)和弹出(pop)操作。
- UITabBarController 是一个标签栏控制器类,用于管理具有多个标签页的界面。它提供了一个标签栏,用户可以通过点击标签切换不同的视图控制器。
- UIPageViewController 是一个分页控制器类,用于管理具有多个页面的界面。它提供了水平或垂直滚动的页面切换效果,用户可以通过滑动或点击页面来切换视图控制器。
- UITableViewController 是一个表格视图控制器类,用于管理表格视图的显示和交互。它提供了一种方便的方式来显示和编辑表格数据,并处理表格视图的委托和数据源方法。
- UIContainerView 是一个容器视图类,用于在一个视图控制器中承载另一个视图控制器。它允许您在一个视图控制器中嵌入其他视图控制器,并管理它们之间的转场和通信。
# 2.3 UINavigationController
先来抛出一个问题:通过 navigationController 跳转到指定的页面无效。为什么没有反应?
PalmWebViewController *vc = [[PalmWebViewController alloc] init];
[self.navigationController pushViewController:vc animated:NO];
2
如果通过navigationController跳转到指定页面没有反应,可能有以下几个原因:
- 确保你的导航控制器已正确设置,并且已经与当前视图控制器相关联。通过以下代码来检查导航控制器是否存在:if (self.navigationController)
- 导航控制器不可见或已隐藏:如果导航控制器当前不可见或已隐藏,跳转可能不会产生任何效果。
# 03.Controller生命周期
UIViewController是iOS开发中常用的视图控制器类,它有着特定的生命周期方法,用于管理视图控制器的创建、加载、显示和销毁过程。以下是UIViewController的生命周期方法的顺序:
- init(nibName:bundle:) 或 init(coder:):初始化方法,用于创建视图控制器的实例。
- loadView():加载视图控制器的视图层次结构。在此方法中,您可以手动创建视图或从nib文件加载视图。
- viewDidLoad():视图已经加载完成,并且可以进行进一步的配置。在此方法中,您可以执行一次性的设置,例如设置数据源、注册通知等。
- viewWillAppear(_😃:视图将要显示在屏幕上,但尚未完成布局。在此方法中,您可以执行一些准备工作,例如更新数据、设置动画效果等。
- viewDidAppear(_😃:视图已经显示在屏幕上,并且已经完成布局。在此方法中,您可以执行一些需要在视图完全可见后才能进行的操作,例如启动定时器、开始动画等。
- viewWillDisappear(_😃:视图将要从屏幕上消失。在此方法中,您可以执行一些清理工作,例如取消定时器、保存数据等。
- viewDidDisappear(_😃:视图已经从屏幕上消失。在此方法中,您可以执行一些额外的清理工作,例如释放资源、取消注册通知等。
- deinit/dealloc:视图控制器即将被销毁。在此方法中,您可以执行最后的清理工作,例如释放内存、取消引用等。
这些生命周期方法提供了在不同阶段执行代码的机会,以便您可以在适当的时机进行初始化、配置、更新和清理操作。了解UIViewController的生命周期方法可以帮助您更好地管理和控制视图控制器的行为。
# 3.1 初始化方法
在iOS应用开发中,UIViewController是视图控制器的基础类,负责视图的管理与交互。初始化方法是视图控制器生命周期的第一个环节,它主要负责设置初始状态和环境。常用初始化方法包括:
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
init?(coder aDecoder: NSCoder)
2
代码逻辑分析:
init(nibName:nibNameOrNil:bundle:) 方法允许开发者指定一个nib文件名(如果有的话),并创建视图控制器。 bundle 参数通常可以不传,默认是nil,意味着使用主程序包。
init?(coder:) 是通过Interface Builder创建视图控制器时调用的初始化方法。 NSCoder 是一个编码器,用来解码存储在nib文件或归档文件中的对象。
# 3.2 加载视图方法
加载视图是视图控制器生命周期中非常重要的一步,涉及到视图的呈现与内容填充。
override func loadView() {
super.loadView()
// 在这里进行自定义的视图加载逻辑
}
2
3
4
代码逻辑分析:
- loadView() 是一个被重写的虚方法,在该方法中可以自定义加载视图的逻辑。这是视图被创建并加载的时机。
- 调用 super.loadView() 可以保证父类的加载逻辑被执行,这是必要的步骤。
扩展性说明:
- 如果视图控制器不使用nib文件来加载视图,则必须重写 loadView 方法,并在其中创建和配置视图。
- 在开发中,如果需要对视图层次结构进行复杂的定制,经常会重写此方法。
# 3.3 视图出现与消失
在视图控制器的生命周期中,视图出现和消失时,系统会调用特定的回调方法,让开发者有机会执行一些额外的逻辑。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 视图即将出现在屏幕上时执行的代码
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 视图即将从屏幕上消失时执行的代码
}
2
3
4
5
6
7
8
9
代码逻辑分析:
- viewWillAppear(_😃 在视图将要出现在屏幕上时调用,此时视图已经在视图层次中,但尚未显示。可以在这里做一些预加载数据的工作。
- viewWillDisappear(_😃 在视图即将从屏幕上消失时调用,此时视图还在视图层次中,但即将被移除。可以在这里做一些清理工作。
扩展性说明:
- 在这两个方法中可以处理一些视图状态转换的逻辑,比如更新导航栏标题、配置页面动画等。
- 需要注意的是,对于异步加载数据的情况,应该确保在视图出现之前加载完毕,以免造成用户体验上的延迟。
# 3.4 视图加载时机
视图加载时机决定了视图层次结构的构建方式,这对于性能和用户交互至关重要。
override func viewDidLoad() {
super.viewDidLoad()
// 在这里进行视图控制器的视图加载完成后的初始化工作
}
2
3
4
代码逻辑分析:
- viewDidLoad() 方法在视图层次结构加载完成后调用。这是初始化视图控制器视图层次结构的绝佳时机。
- 在这个方法中,可以进行视图层次的初始化、子视图控制器的初始化和配置。
扩展性说明:
- 该方法调用后,所有的视图子类已经加载完毕,但视图尚未添加到视图层次中。
- 通常使用 viewDidLoad() 来配置静态的视图组件,如标签、按钮等。
# 3.5 状态变化处理
在视图控制器的生命周期中,需要处理多种状态的变化,以保证应用的稳定性和流畅性。
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// 在这里进行视图消失后的清理工作
}
2
3
4
代码逻辑分析:
- viewDidDisappear(_😃 在视图从屏幕上消失后调用。此时视图层次已经从父视图中移除。
- 这个时机点可以用于执行视图不可见时需要做的操作,例如停止一些动画效果、取消网络请求等。
扩展性说明:
- 视图控制器在消失后依然存在于内存中,直到被系统完全销毁。
- 在此方法中执行清理工作可以帮助释放资源,减少内存泄漏的风险。
# 3.6 系统事件响应
视图控制器不仅要处理视图的生命周期事件,还需要响应用户的交互及系统事件。
override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
super.motionBegan(motion, with: event)
// 在这里处理系统事件,例如设备方向变化
}
2
3
4
代码逻辑分析:
- motionBegan(_:with:) 是一个用来响应系统事件的方法,例如设备的方向变化。
- 重写此方法可以自定义设备方向变化时的响应逻辑,比如旋转动画或更新布局。
扩展性说明:
- 其他系统事件包括但不限于摇晃事件、连续点击事件等。
- 系统事件处理增加了用户交互的复杂性,合理的设计可以提升用户体验。
# 3.7 代码开发建议
- 1、init里不要出现创建view的代码。良好的设计,在init里应该只有相关数据的初始化,而且这些数据都是比较关键的数据。init里不要掉self.view,否则会导致viewController创建view。(因为view是lazyInit的)。
- 2、loadView中只初始化view,一般用于创建比较关键的view如tableViewController的tabView,UINavigationController的navigationBar,不可掉用view的getter(在掉super loadView前),最好也不要初始化一些非关键的view。如果你是从nib文件中创建的viewController在这里一定要首先调用super的loadView方法,但建议不要重载这个方法。
- 3、viewDidLoad 这时候view已经有了,最适合创建一些附加的view和控件了。
- 4、viewWillAppear 这个一般在view被添加到superview之前,切换动画之前调用。在这里可以进行一些显示前的处理。比如键盘弹出,一些特殊的过程动画(比如状态条和navigationBar颜色)。
- 5、viewWillLayoutSubViews 一般用于显示前,对子控件进行布局
- 6、viewWillLayoutSubViews子控件布局完成,可以在这方法里面对子控件进行一些初始化操作.
- 7、viewDidAppear 一般用于显示后,在切换动画后,如果有需要的操作,可以在这里加入相关代码。
- 8、viewDidUnload 这时候viewController的view已经是nil了。由于这一般发生在内存警告时,所以在这里你应该将那些不在显示的view释放了。比如你在viewController的view上加了一个label,而且这个label是viewcontroller的属性,那么你要把这个属性设置成nil,以免占用不必要的内存,而这个label在viewDidLoad时会重新创建。
- 9、接下来看看ViewController中的view是如何被卸载的:当系统发出内存警告时,会调用didReceiveMemoryWarning方法,如果当前有能被释放的view,系统会调用viewWillUnload方法来释放view,完成后调用viewDidUnload方法,至此,view就被卸载了。此时原本指向view的变量要被置为nil,具体操作是在viewDidUnload方法中调用self.myButton = nil;
# 04.Controller内置属性
# 4.1 主要的属性
视图控制器是iOS应用开发中不可或缺的一个重要组件。它管理着一个屏幕的内容展示以及用户的交互操作。
在深入探讨视图控制器的内置属性之前,我们需要了解几个关键属性: view , navigationItem , tabBarItem 等。
- view : 这是视图控制器最重要的属性之一,它是一个UIView对象,作为界面的主视图。所有的用户界面元素都是这个视图的子视图。
- navigationItem : 如果视图控制器被嵌入到UINavigationController中, navigationItem 用于配置导航栏上的标题、按钮等元素。
- tabBarItem : 在使用UITabBarController时, tabBarItem 属性允许开发者为该视图控制器设置底部标签栏的标题、图标和样式。
例如,在 viewDidLoad 方法中,我们通常会初始化这些属性:
override func viewDidLoad() {
super.viewDidLoad()
// 自定义view的初始化
// 配置navigationItem
self.navigationItem.title = "首页"
// 如果有需要,可以添加导航栏按钮等
let rightButton = UIBarButtonItem(title: "编辑", style: .plain, target: self, action: #selector(toggleEditMode))
self.navigationItem.rightBarButtonItem = rightButton
// 配置tabBarItem
self.tabBarItem.title = "设置"
self.tabBarItem.image = UIImage(named: "settingsIcon")
}
2
3
4
5
6
7
8
9
10
11
12
13
# 4.2 属性设置实践
在设置视图控制器的内置属性时,最佳实践是尽可能早地初始化,以避免延迟初始化带来的性能问题和潜在的bug。同时,属性的设置应当遵循简洁、直观的原则。
当涉及到 view 属性时,应当遵循以下步骤:
- 创建子视图 : 在 loadView 方法中创建 view 属性需要的所有子视图。
- 设置约束 : 通过Auto Layout设置子视图的约束,以确保在不同设备上都能正确显示。
- 配置子视图 : 通过编程或Interface Builder设置子视图的样式、添加事件监听等。
对于 navigationItem 和 tabBarItem ,应当根据其角色在对应的生命周期方法中进行配置。例如,在 viewDidLoad 或者 viewWillAppear 方法中。
# 05.Controller导航方法
# 5.1 嵌入与弹出
在iOS应用中,导航控制器是常见的视图控制器容器之一。它允许我们以堆栈的形式管理视图控制器,并提供前进、后退的导航功能。
将视图控制器嵌入导航控制器中是一个常见的操作:
let navigationController = UINavigationController(rootViewController: YourViewController())
self.window?.rootViewController = navigationController
2
在这个例子中, YourViewController() 是一个视图控制器的实例,被设置为导航控制器的根视图控制器。
当需要从导航控制器中弹出当前视图控制器时,可以调用:
self.navigationController?.popViewController(animated: true)
如果需要返回上一级视图控制器,而又不是当前视图控制器,则需要通过导航项进行:
if let previousViewController = self.navigationController?.viewControllers.firstIndex(of: self) {
self.navigationController?.popToViewController(self.navigationController!.viewControllers[previousViewController - 1], animated: true)
}
2
3
# 5.2 推送与弹出
推送一个新的视图控制器到导航控制器堆栈是一种常见的方式来进行页面跳转:
let nextViewController = NextViewController()
self.navigationController?.pushViewController(nextViewController, animated: true)
2
在这个例子中, NextViewController() 代表即将推送到导航堆栈中的视图控制器。
当需要返回到上一个视图控制器时,可以直接调用 popViewController 或 popViewControllerAnimated 方法:
self.navigationController?.popViewController(animated: true)
# 5.3 携带参数并回调
在源视图控制器中,定义一个代理协议,并在目标视图控制器中设置一个代理属性。然后,当目标视图控制器需要回传数据时,可以调用代理方法将数据传回给源视图控制器。
protocol PalmControllerDelegate : AnyObject{
func sendPalmBack(code: Int, data: String)
}
2
3
在目标视图控制器中设置代理属性和参数属性:
weak var delegate: PalmControllerDelegate?
var userId: String? = "";
var userName: String? = "";
var appId: Int = 0;
2
3
4
第一步:在源视图控制器中,跳转目标控制器,并且携带参数,设置代理如下所示:
private func toNavController() {
print("开始跳转目标页面,并且携带参数");
let viewController = NavigationDemoController()
viewController.delegate = self;
viewController.userId = "13667225184"
viewController.userName = "用户名称"
viewController.appId = 100;
self.navigationController?.pushViewController(viewController, animated: true)
}
2
3
4
5
6
7
8
9
第二步:在目标控制器,处理回传数据,然后通知源控制器接受数据,代码如下:
//目标控制器
let backCode = 0;
let backData = "Hello from SecondViewController!"
delegate?.sendPalmBack(code: backCode, data: backData)
navigationController?.popViewController(animated: true)
//源控制器,一定要遵循代理协议,就是最开始定义的PalmControllerDelegate
class YourController: UIViewController ,PalmControllerDelegate {
//省略部分代码
//处理刷掌sdk返回的状态
//code是状态码
//data是数据
func sendPalmBack(code: Int, data: String) {
print("开始打印回调参数");
print(code);
print(data)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 06.视图层次管理构建
# 6.1 子视图添加&移除
在开发过程中,我们经常需要在父视图控制器中添加或移除子视图控制器。这种方式可以让我们更好地管理复杂的界面逻辑,同时保持代码的清晰和组织性。
代码块演示如何添加子视图控制器:
class ParentViewController: UIViewController {
func addChild(_ viewController: UIViewController) {
self.addChild(viewController)
self.view.addSubview(viewController.view)
viewController.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
***Anchor.constraint(equalTo: ***Anchor),
viewController.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
viewController.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
viewController.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
])
}
}
// 使用时
let childVC = ChildViewController()
parentVC.addChild(childVC)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在上述代码中,我们首先调用了父视图控制器的 addChild(😃 方法,该方法会调用到UIKit框架的 addChild(😃 方法,以确保视图控制器的生命周期被正确管理。
然后,将子视图控制器的视图添加到父视图控制器的视图层次中,并使用Auto Layout约束来定义子视图控制器的视图如何填充父视图的边界。
# 6.2 视图层次布局与约束
良好的视图布局和约束设置能够确保应用在不同设备和屏幕尺寸上均能正确显示。使用Auto Layout是管理视图层次结构中视图间关系的推荐方式。
约束类型 描述 基线约束 (baseline) 设置两个控件基线间的距离 边距约束 (margins) 控件与其父视图边缘的距离 中心点约束 (center) 控件中心点与父视图中心点的对齐 大小约束 (size) 控件的宽度和高度 领域约束 (priority) 控制约束的优先级,解决冲突时的优先顺序
使用Auto Layout时,我们通常需要考虑约束冲突的解决策略,即当多个约束条件不一致时,系统如何决定最终的布局状态。这通常需要开发者在设置约束时给约束设置优先级,并通过调试来确保布局效果符合预期。
# 6.3 控件添加与布局
在iOS开发中,添加和布局控件是最基本的操作。下面以Swift代码块示例,展示如何为一个简单的登录界面添加按钮和文本字段,并进行布局。
class LoginViewController: UIViewController {
private lazy var usernameTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Username"
return textField
}()
private lazy var passwordTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Password"
textField.isSecureTextEntry = true
return textField
}()
private lazy var loginButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Login", for: .normal)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(usernameTextField)
view.addSubview(passwordTextField)
view.addSubview(loginButton)
// 使用NSLayoutConstraint为控件设置布局
NSLayoutConstraint.activate([
***Anchor.constraint(equalTo: ***Anchor, constant: 20),
usernameTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
usernameTextField.widthAnchor.constraint(equalToConstant: 200),
***Anchor.constraint(equalTo: usernameTextField.bottomAnchor, constant: 10),
passwordTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
passwordTextField.widthAnchor.constraint(equalToConstant: 200),
***Anchor.constraint(equalTo: passwordTextField.bottomAnchor, constant: 20),
loginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
loginButton.widthAnchor.constraint(equalToConstant: 100)
])
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
上述代码块展示了如何在 viewDidLoad() 方法中添加控件并使用Auto Layout来设置它们的布局约束。
代码中使用了 NSLayoutConstraint.activate(_😃 来激活一组约束,并使用了 leadingAnchor 、 trailingAnchor 、 topAnchor 、 bottomAnchor 和 centerXAnchor 等方法来定义控件的相对位置。
# 6.4 数据绑定UI
数据绑定是将数据源直接绑定到UI元素上,以便UI可以显示最新的数据。这在构建动态内容时尤为重要。在iOS中,通常通过数据源协议(例如UITableView的数据源)来实现数据与UI的绑定。
代码块演示如何将数据绑定到表格视图(UITableView):
class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var dataArray = ["One", "Two", "Three"] // 数据数组
@IBOutlet weak var tableView: UITableView!
// 数据源方法实现
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
cell.textLabel?.text = dataArray[indexPath.row] // 数据绑定到cell的textLabel
return cell
}
// 动态更新UI方法示例
func updateUI() {
dataArray.append("Four") // 向数据数组添加新数据
tableView.reloadData() // 重新加载表格视图,更新UI
}
}
// 在某处调用updateUI方法
myViewController.updateUI()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
在这个简单的例子中, dataArray 变量被设置为包含几个字符串的数组,这个数组充当数据源。 tableView(_:cellForRowAt:) 方法中的 dataArray[indexPath.row] 将数组中的字符串直接绑定到表格视图单元格的 textLabel 属性上。
通过调用 tableView.reloadData() ,当数据源发生变化时,表格视图将刷新其内容,显示最新的数据。这是一个常见的动态更新用户界面的策略。
# 07.手势识别器应用
# 7.1 手势识别器的类型
手势识别器(Gesture Recognizer)是iOS开发中的重要组件,用于捕捉用户的触摸手势,并将其转换成可识别的动作。
常见的手势识别器类型包括
- UITapGestureRecognizer (轻击)
- UIPinchGestureRecognizer (捏合)
- UIRotationGestureRecognizer (旋转)
- UISwipeGestureRecognizer (滑动)
- UILongPressGestureRecognizer (长按)
每种手势识别器都有其特定的使用场景。例如,UITapGestureRecognizer 适用于按钮点击,而 UIPinchGestureRecognizer 适合于实现图片的缩放功能。UISwipeGestureRecognizer 可用于左右滑动页面浏览, UIRotationGestureRecognizer 适用于旋转操作,而 UILongPressGestureRecognizer 则用于检测长按事件。
# 7.2 手势与视图交互
在视图控制器中使用手势识别器,需要在 viewDidLoad 方法中创建并配置手势识别器,然后将其添加到视图中。以下是添加轻击手势识别器的示例代码:
override func viewDidLoad() {
super.viewDidLoad()
// 创建轻击手势识别器
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
// 配置手势识别器的属性(可选)
tapRecognizer.numberOfTapsRequired = 1
// 将手势识别器添加到视图中
self.view.addGestureRecognizer(tapRecognizer)
}
@objc func handleTap(_ sender: UITapGestureRecognizer) {
if sender.state == .began {
print("轻击手势识别成功")
// 处理轻击事件的具体逻辑
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
手势识别器可以与视图控制器中的其他UI元素交互,例如,可以禁用按钮的默认点击事件,改为使用手势识别器来控制。
# 08.控制器高级应用
# 8.1 模态展示与解除
模态视图控制器的呈现与解除。模态展示(Modal Presentation)是iOS中一种常见的视图展示方式,用于在当前视图上临时展示另一个视图。模态视图的展示和解除通常涉及 present(_:animated:completion:) 和 dismiss(animated:completion:) 方法。
// 展示模态视图控制器
let modalViewController = ModalViewController()
self.present(modalViewController, animated: true, completion: nil)
// 解除模态视图控制器
modalViewController.dismiss(animated: true, completion: {
print("模态视图控制器已解除")
})
2
3
4
5
6
7
8
模态转换动画与无动画的切换。iOS为模态展示提供了多种动画样式,例如 UIModalTransitionStyle.flipHorizontal (翻转)、 UIModalTransitionStyle.coverVertical (覆盖)等。开发者可以通过设置 modalPresentationStyle 属性来选择不同的展示动画。
若需要无动画地切换视图控制器,可以将动画参数设置为 false 。
// 无动画呈现模态视图控制器
self.present(modalViewController, animated: false, completion: nil)
// 无动画解除模态视图控制器
modalViewController.dismiss(animated: false, completion: nil)
2
3
4
5
# 8.2 视图状态保存与恢复
视图状态的保存是通过 UIViewController 的 EncodeRestorableState 和 DecodeRestorableState 协议实现的。
在 viewWillDisappear 或 viewDidUnload 方法中保存状态,然后在 viewDidLoad 中恢复状态。这些方法允许视图控制器保存和恢复其视图层次结构和状态信息。
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 保存视图状态
UserDefaults.standard.set(self.view.frame, forKey: "viewFrame")
}
override func viewDidLoad() {
super.viewDidLoad()
// 恢复视图状态
if let viewFrame = UserDefaults.standard.object(forKey: "viewFrame") as? CGRect {
self.view.frame = viewFrame
}
}
2
3
4
5
6
7
8
9
10
11
12
13
在视图控制器生命周期中, viewWillAppear 方法是在视图即将展示之前调用的。可以在这个方法中加载视图状态,确保用户返回到该视图控制器时,看到的是他们之前离开时的状态。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 确保视图状态恢复
if let viewFrame = UserDefaults.standard.object(forKey: "viewFrame") as? CGRect {
self.view.frame = viewFrame
}
}
2
3
4
5
6
7
# 8.3 UINavigationController
UINavigationController 是一个管理视图控制器堆栈的控制器,它提供了视图控制器之间的导航管理。
在使用 UINavigationController 时,可以使用 pushViewController(_:animated:) 方法来推送新的视图控制器,用 popViewController(animated:) 来弹出当前视图控制器。
// 推送新的视图控制器
let newViewController = NewViewController()
self.navigationController?.pushViewController(newViewController, animated: true)
// 弹出当前视图控制器
if let navigationController = self.navigationController {
navigationController.popViewController(animated: true)
}
2
3
4
5
6
7
8
在导航控制器中,还可以利用 setNavigationBarHidden(_:animated:) 方法来隐藏或显示导航栏。对于复杂的导航流程,可以在视图控制器之间共享数据,通过 prepare(for:sender:) 方法进行视图控制器跳转前的准备工作。
// 隐藏导航栏
self.navigationController?.setNavigationBarHidden(true, animated: true)
// 显示导航栏
self.navigationController?.setNavigationBarHidden(false, animated: true)
2
3
4
5
# 8.4 视图切换动画示例
无动画操作的实现代码示例。通过设置 animated 参数为 false ,可以在推送或弹出视图控制器时,实现无动画的效果。
// 推送新的视图控制器但不显示动画
self.navigationController?.pushViewController(newViewController, animated: false)
// 弹出当前视图控制器但不显示动画
if let navigationController = self.navigationController {
navigationController.popViewController(animated: false)
}
2
3
4
5
6
7