GeometryReader
是一个通过闭包来构建视图的容器,可以返回一个 GeometryProxy
类型的结构体,它包含如下属性和方法,由此我们可以获取当前视图容器(即父视图)的尺寸和位置,绘制以其为参考坐标系的视图。
1 | var safeAreaInsets: EdgeInsets |
比如,我们需要绘制一个长宽均为父视图一半的矩形:
1 | struct ContentView: View { |
GeometryReader
是一个通过闭包来构建视图的容器,可以返回一个 GeometryProxy
类型的结构体,它包含如下属性和方法,由此我们可以获取当前视图容器(即父视图)的尺寸和位置,绘制以其为参考坐标系的视图。
1 | var safeAreaInsets: EdgeInsets |
比如,我们需要绘制一个长宽均为父视图一半的矩形:
1 | struct ContentView: View { |
在学习 SwiftUI 的过程中,发现对标题中的内容还不甚熟悉,而这些内容是 SwiftUI 中极其重要的部分,不理解就很难熟练地掌握 SwiftUI,故温习并记录,以下内容可视为官方教程的简装版。
协议规定了实现某一特定功能的方法和属性。
类、结构体、枚举类型都可以遵循协议。
1 | protocol SomeProtocol { |
属性可以是存储型或计算型,必须明确其可读写性。
1 | protocol Cat { |
@StateObject 修饰的对象与 @ObservedObject 一样,都需要遵循 Observable 协议,功能也类似。区别在于,@StateObject 修饰的对象只会在所属的 View 中创建一次并在 View 的生命周期内存储相应的状态,而 @ObservedObject 修饰的对象会随着 View 的重绘生成新的对象,不会在 View 的生命周期内存储该对象的状态。
1 | class Counter: ObservableObject { |
如上代码所示,当我们点击 CounterView1 和 CounterView2 中的按钮时,会给相应的 Counter 实例的 count 属性加 1,一旦我们点击 “Tap me” 按钮,View 会执行重绘,这时 CounterView2 中的 count 会重置为 0,而 CounterView1 中使用 @StateObject 修饰的对象中的 count 仍然持有当前的数据状态。
那么我们应该在什么场景下分别使用这两个属性包装器呢?
在 View 的生命周期内,需要一直持有并存储对象的状态时,使用 @StateObject 修饰。基本上,绝大多数情况下的 viewModel 都会是这种情况。
只有在少数情况下,View 不需要一直持有该对象,该对象的状态会随着外界的条件改变而刷新自己时,我们才用到 @ObservedObject 修饰,比如上面的例子,如果我们要求点击 “Tap me” 按钮时,count 就重置,就需要用 @ObservedObject 修饰了。
Demo
由 SwiftUI 管理的可读写的属性包装器,当修饰的属性值改变的时候,界面会随之更新。由 @State 包装的属性通常用 private 修饰,在 body 内使用。
下面的实例是一个可以切换天气的界面,并且可以控制天气是否可变。
1 | enum Weather: String, CaseIterable { |
接下来,我们用自定义的 WeatherView 去展示天气图片,图片会跟随父视图天气变化做相应改变,并且在 WeatherView 中可以通过点击改变天气。这个时候,我们就需要用到 @binding 来做数据的双向绑定。
最近提交的 App 遇到了这个问题,邮件中提到的是被废弃的 UIWebView API,在工程中全局搜索后没有找到 UIWebView。想来必然是第三方静态库使用了,在工程目录下使用命令grep -r UIWebView .
查看哪些库使用了 UIWebView,移除或升级相关库即可。
项目本身设置了 Valid Architecture 为 arm64、arm64e,但是因为使用了支持 32 位设备的三方库,所以生成了冗余的 symbols 文件。
查询 symbols 文件的生成情况:Xcode Window -> Organizer 选择有问题的 archive,右击选择 Show in finder,命令行进入 .app 中的 dSYMs 文件夹,执行 `dwarfdump –uuid ` 可以查询到是否生成了多余的文件。
解决方法是在 Podfile 中添加如下内容:
1 | post_install do |installer| |
在platform :ios, '12.0'
这一行下面添加即可,然后重新pod install --verbose
。
虽然 Xcode 自带格式对齐(Ctrl + i),但是在多人协作的情况下,就很难保证编码风格的统一了,SwiftFormat就是解决这一问题的好工具。
SwiftFormat
具有多种使用方式,目前我在实际工作中使用的是CocoaPods
集成。具体使用方法:
pod 'SwiftFormat/CLI'
,安装 SwiftFormat
TARGETS - Build Phases - New Run Script Phase
,添加如下脚本:
1 | "${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat" . --exclude Pods,Generated |
在编译或运行的时候,SwiftFormat
会对齐不符合格式的代码。Enjoy it~
首先,创建我们的自定义模板文件夹:
1 | mkdir -p ~/Library/Developer/Xcode/Templates/Custom |
然后复制系统提供的模板文件,路径如下:
1 | /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File Templates/Source/Swift File.xctemplate |
将Swift File.xctemplate
整个文件夹复制到刚才创建的Custom
文件夹。
然后我们开始自定义,只需要修改___FILEBASENAME___.swift
中的内容即可。
其中涉及到一些宏定义可以参考:Text macros reference。注意:宏定义前后必须加上三个下划线。
然后我们就可以使用了,如图:
首先,用 Xcode 创建一个 plist 文件:IDETemplateMacros.plist。
在新建的 plist 文件中,添加键值对。key 为 FILEHEADER
,value 参考 IDETemplateMacros.plist中的内容。
其中,FILENAME
、PACKAGENAME
等为宏定义,更多宏定义请可以通过 Xcode - Help 搜索“Text macros reference”获得。这里要注意的是,宏定义的前后必须要加上三个下划线。
然后我们将编辑好的 plist 文件放入指定的路径,关于路径有以下选择,对应不同的作用范围:
Project - 某个用户
1 | <ProjectName>.xcodeproj/xcuserdata/[username].xcuserdatad/ |
Project - 所有用户
1 | <ProjectName>.xcodeproj/xcshareddata/ |
Workspace - 某个用户
1 | <WorkspaceName>.xcworkspace/xcuserdata/[username].xcuserdatad/ |
Workspace - 所有用户
1 | <WorkspaceName>.xcworkspace/xcshareddata/ |
Xcode -全局
1 | ~/Library/Developer/Xcode/UserData/ |
所有的工作已经完成了,试试创建一个新的文件看看效果吧!
使用命令卸载:
1 | sudo gem uninstall cocoapods |
最新版本的 MacOS 可能会提示错误:
1 | ERROR: While executing gem ... (Gem::FilePermissionError) |
用以下命令可以解决:
1 | $ sudo gem uninstall -n /usr/local/bin cocoapods |
然后查看本机安装的 cocoapods 相关的东西:
1 | $ gem list --local | grep cocoapods |
一般会有如下显示:
1 | cocoapods-core (1.9.1, 1.8.4) |
然后使用命令逐个删除,比如:
1 | $ sudo gem uninstall cocoapods-core |
或
1 | sudo gem uninstall -n /usr/local/bin cocoapods-core |
卸载完成后重新安装即可。