2个问题:1、怎么开发;2、怎么用在现有的项目中?
开发
资源
https://developer.apple.com/tutorials/swiftui:官方教程,有好几个章节,并且有配套的example
https://onevcat.com/2019/06/swift-ui-firstlook/:喵神写的《SwiftUI 的一些初步探索》
SwiftUI app 的启动
教程示例 app 在 AppDelegate 中通过 application(_:configurationForConnecting:options) 返回了一个名为 “Default Configuration” 的 UISceneConfiguration 实例:
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration
{
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
这个名字的 Configuration 在 Info.plist 的 “UIApplicationSceneManifest -> UISceneConfigurations” 中进行了定义,指定了 Scene Session Delegate 类为 $(PRODUCT_MODULE_NAME).SceneDelegate。这部分内容是 iOS 13 中新加入的通过 Scene 管理 app 生命周期的方式,以及多窗口支持部分所需要的代码。这部分不是我们今天的话题。在 app 完成启动后,控制权被交接给 SceneDelegate,它的 scene(_:willConnectTo:options:) 将会被调用,进行 UI 的配置:
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions)
{
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = UIHostingController(rootView: ContentView())
self.window = window
window.makeKeyAndVisible()
}
这部分内容就是标准的 iOS app 启动流程了。UIHostingController 是一个 UIViewController 子类,它将负责接受一个 SwiftUI 的 View 描述并将其用 UIKit 进行渲染 (在 iOS 下的情况)。
UIHostingController 就是一个普通的 UIViewController,因此完全可以做到将 SwiftUI 创建的界面一点点集成到已有的 UIKit app 中,而并不需要从头开始就是基于 SwiftUI 的构建。
- 是吗,也就是说,可以在现有项目中,使用UIHostingController,就能使用 SwiftUI ?
- UIScene: 需要进一步了解!
关于 some View
some View 这种写法使用了 Swift 5.1 的 Opaque return types 特性。它向编译器作出保证,每次 body 得到的一定是某一个确定的,遵守 View 协议的类型,但是请编译器“网开一面”,不要再细究具体的类型。
具体看喵神的文章,写的一清二楚
UI
搭积木式的开发,参考官方sample
数据流
- 在View的内部,可以使用state,跟react很像
@State var showFavoritesOnly = false
- 数据绑定BindableObject
仅仅是data肯定是不能绑定ui的,所以RxSwift使用Observable,ui使用的是Observable里面的数据,而不是直接使用data,这样observable变化,ui可以实时绑定更新。BindableObject也是一样的道理。
开始觉得:在Landmarks的代码中,很明显SwiftUI使用的是双向绑定,并不是单向数据流,整个数据流程,跟RxSwift差别还是挺大的,虽然看起来简洁,但是感觉乱糟糟的一团,尤其是LandmarkDetail,传进来一个landmark,还要再从userData里面去获取对应的index,需要去动态改userData里面的landmark,而不是列表点击传进来的landmark
后来觉得:好像SwiftUI某些更简洁,某些时候也很复杂,ui使用BindableObject的数据,都不用订阅,只要BindableObject数据变化,send一下,UI就能刷新,好像是更简洁。但是在detail页面,又要通过index去取BindableObject的数据,有点复杂化了,不知道有没有好办法。
其他
- 2种方法来让list所用的数据变成 identifiable
Lists work with identifiable data. You can make your data identifiable in one of two ways: by calling the identified(by:) method with a key path to a property that uniquely identifies each element, or by making your data type conform to the Identifiable protocol.
struct Landmark: Hashable, Codable {
var id: Int
}
List(landmarkData.identified(by: \.id)) { landmark in
}
struct Landmark: Hashable, Codable, Identifiable {
var id: Int
}
List(landmarkData) { landmark in
}
- vector based 的图片,可以随意修改前景色,不是说有的都可以吗?
Because system images are vector based, you can change their color with the foregroundColor(_:) modifier.
- list
// 只显示数据列表
List(landmarkData) { landmark in
if !self.showFavoritesOnly || landmark.isFavorite {
NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
}
// 加一些类似headerview,list就不能直接使用landmarkData参数了,而是在闭包里面forEach
List {
Toggle(isOn: $userData.showFavoritesOnly) {
Text("Show Favorites Only")
}
ForEach(userData.landmarks) { landmark in
if !self.userData.showFavoritesOnly || landmark.isFavorite {
NavigationButton(
destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
}
}
- Button这里为什么是Label,搞不懂
public init(action: @escaping () -> Void, label: () -> Label)
Button(action: {
withAnimation {
self.showDetail.toggle()
}
}) {
Image(systemName: "chevron.right.circle")
.imageScale(.large)
.rotationEffect(.degrees(showDetail ? 90 : 0))
.scaleEffect(showDetail ? 1.5 : 1)
.padding()
}
-
原来SwiftUI的定义在一个文件里面,10000多行,Foundation也是一样,有点意思,为什么
-
真个动画这一节不太明白,有空再细细研究
-
sample都编译不通过,我还玩个球