引言
由于近一两月才上手iOS开发,对iOS开发相关的知识也仅是参加内部培训系统的学习了一个月而已,期间整理并输出了一系列的文章:iOS 入门系列
但是,在真正上手项目开发后,我深深的体会到:短期的学习,确实让我了解了iOS 开发
,但是真正开发的时候就会发现基础掌握的不牢固,对于一些知识点的理解不是很清晰,或者直接说就是不知道
本篇文章的目的就是将我在开发过程中概念比较模糊的知识点罗列,并按照我的理解进行讲解,由于开发使用的是UIkit 框架
,因此本篇文章是围绕其展开叙述的
根视图到底是什么?
最近,被一个概念给搞懵了:根视图到底是什么?最初我把它理解成栈的形式,最底端的那个视图就是根视图吧?其实,这样的理解是完全正确的,但是渐渐发现这个貌似又不太对......
在写了一个案例验证之后才发现:原来不是我理解的有问题,而是我对基础掌握的不是太牢固
必备知识点
在理解根视图之前,先了解一下与根视图相关的一些知识点,所谓打好基础便是如此,理解了这些知识概念一切就会变得通俗易懂了~
UIApplication
UIApplication
不直接管理视图,而是用于管理应用程序的生命周期、事件处理和管理应用程序级别的设置和协作等功能。它没有根视图,但它会创建应用程序的主窗口,并将根视图控制器(通常是一个UIViewController
)添加到主窗口上。这个根视图控制器将成为应用程序的初始展示页面
UIApplication
通过keyWindow
属性提供了对主窗口 (UIWindow
) 的访问
UIWindow
UIWindow
是一个表示应用程序的主窗口的类。每个iOS应用程序都有一个主窗口,它是应用程序中所有视图层次结构的顶级容器
UIWindow
的主要作用是托管应用程序的视图层次结构,并提供一个可视化容器,用于显示应用程序的用户界面。它是视图控制器的容器,用于管理视图控制器的层次结构,并将其内容显示在屏幕上
根视图控制器 rootViewController
这是应用程序的初始展示页面,通常是一个UIViewController
子类。它是整个应用的第一个视图控制器,用户将从这里开始应用程序的导航。根视图控制器可以是一个普通的视图控制器,也可以是一个容器视图控制器(如UITabBarController
或UINavigationController
)
标签栏控制器 UITabBarController
UITabBarController
是一个容器视图控制器,用于管理多个子视图控制器,每个子视图控制器对应一个标签页。它的根视图是标签页中的一个,用户可以通过标签切换不同的子视图控制器
导航栏控制器 UINavigationController
UINavigationController
也是一个容器视图控制器,用于实现导航堆栈。它的根视图是堆栈中的第一个视图控制器。用户可以从这个根视图导航到其他视图控制器,然后通过导航栏上的返回按钮返回到根视图
可视化解析
如上图所示的视图结构,storyboard
场景入口(在 iOS之UIkit 中有关于场景入口的介绍)是标签栏控制器(根视图控制器),然后它下面有两个子视图,其中导航栏控制器也是容器视图控制器,下面又接着两个子视图
好的,继续回到根视图这个话题,rootViewController
只有一个,是程序的初始展示页面,可以通过主窗口访问(例如:UIApplication.shared.keyWindow?.rootViewController
),在上图中就是TabBarVC
对于标签栏控制器和导航栏控制器这两个容器视图控制器,它们也是存在根视图的,上图中的TabBarVC的根视图在我的程序中是NavigationVC,而NavigationVC的根视图可以很明确的看出是VC1
拓展
UIApplication
和UIWindow
是UIKit框架中的两个核心组件,它们协同工作以实现应用程序的生命周期管理和用户界面的展示。UIApplication
管理应用程序级别的事务,而UIWindow
管理应用程序的用户界面的显示,我目前的理解:类似h5的main.js
和index.html
小结
根视图控制器负责应用程序的主要界面,可以包含其他视图控制器或视图;而容器视图控制器通常使用导航堆栈来管理多个子视图控制器的切换,以便用户可以浏览和返回不同的页面。这种导航堆栈的概念在UINavigationController
中特别明显,因为它管理一个线性的视图控制器堆栈
约束又如何去理解?
好的,通过上面的描述,对于根视图相关的概念大致都已经搞明白了,然后接下来就是涉及到页面布局的知识,这也是iOS 开发
必须牢牢掌握的基础知识,因为前端页面的开发页面布局的权重还是比较高的
必备知识
要理解约束,首先得知道UIkit
中的常用的容器视图和视图有哪些,因为约束就是发生在它们身上的
容器视图
容器视图是一种特殊类型的视图,它可以包含其他视图(视图控件或容器视图)。容器视图的作用是将多个视图组织在一起,形成一个整体。容器视图可以控制其子视图的布局和排列方式
常见的容器视图:
UIView
:UIView
本身也可以用作容器视图,将子视图添加到UIView
中,并通过手动设置它们的位置和大小来创建自定义布局UIStackView
:UIStackView
是一个非常强大的容器视图,用于实现堆叠布局,可以按照水平或垂直方向排列其子视图UIScrollView
:UIScrollView
是一个滚动容器,用于显示内容较多的视图,用户可以通过滚动来查看全部内容UICollectionView
:UICollectionView
是用于创建复杂布局的容器,特别适合显示类似网格的数据UITableView
:UITableView
用于创建表格式布局,用于显示列表数据UIPageViewController
:UIPageViewController
用于创建页面切换的容器,适用于创建引导页、滑动图片浏览器等应用
视图
视图是 iOS 用户界面的基本构建块。它可以是按钮、标签、图像、文本框等等。每个视图都有自己的属性和行为,例如位置、大小、背景颜色、文本内容等
常用的视图控件:
- UILabel:用于显示文本标签
- UIButton:按钮视图,用于触发操作或执行操作
- UIImageView:显示图像或图标的视图
- UITextField 和 UITextView :文本输入视图,用于用户输入文本
- UITextField适合需要单行文本输入的情况,例如:登录、搜索、注册表单等
- UITextView适合需要多行文本输入的情况,例如:聊天应用中的消息输入、日记编辑、评论框等
可视化解析
如上图所示,在父容器视图UIView
内部包含有三个子视图:UIView
、UILabel
和UIButton
,这些子视图之间可以通过约束进行相对布局,也可以直接与父容器通过约束进行相对布局。对于一个视图,它包含上下左右四个方向,与其他视图建立约束时需要明确是哪个方向
然后在设置约束时有inset 和offset 两种偏移方向,一个是内部偏移、一个是外部偏移,可以类比h5 布局的内边距(padding)和外边距(margin)
举例说明一下:UIButton
的顶部与父视图UIView
建立约束,那么正常情况下就是UIButton
的顶部与UIView
的顶部形成约束,这样UIView
的高度就是自动布局而不是写死了,其他方向的约束同理,这样就通过约束实现了自动布局的效果
拓展
当然,你可能会问:我想让UIButton
的顶部和父视图UIView
的底部形成约束不行吗?非得顶部就和顶部建立约束?
我的回答是当然可以,但是完全不建议,既然要和父视图底部建立约束,用UIButton
的底部不是更显而易懂?所以得出一个结论:在建立约束时,建议是在同方向之间
小结
经过上面的描述,大概也能理解约束这个概念了吧?简单点来说就是通过约束来实现父视图的自动布局以及子视图间的相对布局
至于我为什么会在这里卡住,大概是因为它和h5页面的布局写法差别有点大,再加上接触不久吧,用习惯了就不难理解了~
delegate 就是一个属性?
前言
delegate
(在 Swift 那些特有的语法结构 中的协议处有关于代理的介绍)在iOS 开发
中应该属于重要并且相对难理解的知识点之一了吧
在我刚接触到代理以及接触了一段时间后,它给我的直观感受便是:这是类的一个属性?在经过一番查阅后,我发现它并不是看起来那么简单......
首先,可以将它理解为是一个属性,但是它是实例属性,因为不同的实例其值可以不同;然后,它的类型也比较特殊,类型是一个协议
在这些定义好之后,就可以给类实例的delegate 属性
进行赋值了。当然它赋值的对象也比较特殊,得是一个遵循了与之相同协议的实例对象
后续
说实话,刚开始我不是很理解,因为在iOS 开发
中协议就类似于接口,那不是直接实现接口(遵循协议)即可?为什么还要加一个代理?有什么区别吗?在某一次灵光一闪,画了一张图之后,我感觉我有些悟了,哈哈哈,然后回过头去看使用它的一些优点,也慢慢的理解到了
下面附上我所说的图: 上图中,我还将继承也给画了出来,虽然不是很相关,只是区别一下其与接口而已;主要的还是看接口和协议代理的区别:代理其实就是实现接口的拓展版,更具模块化了,当协议需要修改时,只需要改动遵循协议的类即可,然后代理给需要使用的原始类,无论协议如何修改,原始类都不会受到影响
协议的优点:
- 松散耦合:代理模式允许对象之间的关系更加松散,一个对象可以委托其它对象来处理某些任务,而不需要了解具体的实现细节
- 单一职责原则:代理模式有助于遵循单一职责原则,即每个对象应该只负责一项任务。通过将任务委托给代理对象,原始对象可以专注于其主要职责,而代理可以专注于处理委托的任务
- 模块化和可维护性:代理模式使代码更具模块化,易于维护和扩展。新的代理对象可以轻松地添加,而不会影响到原始对象
- 接口隔离:代理模式有助于隔离接口,使得客户端对象只需知道代理的接口而不需要了解具体实现