Swift-ui 2
10/22
How to use ObservableObject/@Published
class Contact: ObservableObject {
@Published var name: String
@Published var age: Intinit(name: String, age: Int) {
self.name = name
self.age = age
}func haveBirthday() -> Int {
age += 1
return age
}
}let john = Contact(name: "John Appleseed", age: 24)
cancellable = john.objectWillChange
.sink { _ in
print("\(john.age) will change")
}
print(john.haveBirthday())这个属于combine的内容, 来自这儿的几点建议:
- Use @State for simple properties that belong to a single view. They should usually be marked private. @State局限于一个view,不要用于接口
- Use @ObservedObject for complex properties that might belong to several views. Any time you’re using a reference type you should be using @ObservedObject for it.ObservedObject用于几个view之间共享数据
- Use @EnvironmentObject for properties that were created elsewhere in the app, such as shared data. 这个更适合用于swiftui与外部的接口,包括presenterView有一个扩展
@inlinable public func environmentObject<B>(_ bindable: B) -> some View where B : ObservableObject
从名字上可以看到属于环境变量
EnvironmentObject是swiftui,而ObservableObject属于combine。@frozen @propertyWrapper struct EnvironmentObject<ObjectType> where ObjectType : ObservableObject
@frozen struct LocalizedStringKey根据这儿的描述,
- 我们可以直接提供一个Localizable.strings并添加: "key"="value"
- 然后就可以在直接使用如 Label("key)
- 但是我们在实际中发现一些问题,比如在一个pod而不是app中
10/22 <-
font: OTF vs TTF
OTF stands for Postscript-flavored OpenType.
TTF stands for Truetype-flavored Opentype.
各有利弊吧
padding() default values
default values are not (0,0,0,0), might be 20 for each
padding(.bottom, -130) other part will be 0
Spacer/Divider
虽然两者都conforms to View, 但是他们并不像View,比如没有背景色
Changes the view’s proposed area to extend outside the screen’s safe areas.
ios system font size
Style Weight Size (points) Leading (points)
Large Title Regular 34 41
Title 1 Regular 28 34
Title 2 Regular 22 28
Title 3 Regular 20 25
Headline Semi-Bold 17 22
Body Regular 17 22
Callout Regular 16 21
Subhead Regular 15 20
Footnote Regular 13 18
Caption 1 Regular 12 16
Caption 2 Regular 11 13
title vs headline
Title is the name of the whole book or article
Headline is for chapter
Image(room.thumbnailImage).resizable()
.frame(width: 32.0, height: 32.0)有趣的是这个地方并不需要resizable也能设frame
将UIKit View包装成 swiftui的 view
A wrapper for a UIKit view that you use to integrate that view into your SwiftUI view hierarchy.protocol UIViewRepresentable : View where Self.Body == Never
这儿的意思是这个View不会有body
.navigationBarTitle(Text(“”), displayMode: )
这个方法已经被废弃。其中mode有:
- automatic:取决于前一个页面,可能是inline/large
- inline: 在一行
- large: title下下面 加大字码, 默认参数选择inline虽然紧凑,但是背景无法全透明,而large就是全透明。因此废弃也是一件好事。不带displayMode参数的方法没有废除,也就是是说废弃了inline这个参数而已。更多的与Navigation Bar相关的方法
- func navigationBarHidden(Bool) -> some View
- func statusBar(hidden: Bool) -> some View比较有趣的是这个:
func navigationTitle<V>(_ title: () -> V) -> some View where V : View ,可以使用一个自定义的view。
10 direct subviews
一个container最多只能有10个直接的subviews,否则会出现编译错误。我估计是这个函数只写了十个参数 哈哈
- 可以通过Group 避免
- List没有这个限制
- 应该是一种静态限制(也就是编译的时候检查)
init
当自定义View的时候,可以给View添加一个init函数,当然可以带参数,实现的View是一个struct,只是conforms to View
Carousel实现
看到apple给出的一个例子是:ScrollView + HStack + ForEach
- List 只能vertical
- ScrollView 可以是两个方向
ForEach vs List
- List is a View, but ForEach is not.
Toolbar
Toolbar变得很有意思,比如支持NavigationBar
.toolbar {
ToolbarItem(placement: .navigation) { Button("X") { }}
}
之前我们一般是 .bottomBar
这儿有一个Image 的tut
在UIKit的体系里面集成swiftui的View,也就是把一个swiftui的View包装在一个UIHostintController下,让这个View作为VC的view。
这儿说了如何集成
- let controller = UIHostingController(rootView: ...)
-
addChild(controller)
view.addSubview(controller.view)
controller.didMove(toParent: self)
----
比较有意思的这个UIHostingController有好几个init方法,其中一个是
init(nibName: String?, bundle: Bundle?)
言下之意是 这个UIHostingView不仅可以用在swiftui上,还可以用在传统的uikit上。
----
action需要额外处理,比如参数等,会有点麻烦,需要形成统一的处理方式的话会好一点
UIViewControllerRepresentable UIViewRepresentable
这个接口是把一般的UIView包装成一个swift-ui的View,注意,是UIView而不是UIViewController。这个定义很奇怪:我估计是可以实现一个任意类型的Coordinator。这儿说Void是默认类型
associatedtype Coordinator = Voidfunc makeCoordinator() -> Self.Coordinator
Coordinator的意思是协调人,第三方---
tutorial 里面的MapView
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Context) {
let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
- func makeUIView(context: Self.Context) -> Self.UIViewType 这个是范型和associatetype
- Context: 这就是前面那个coordinate,没有实现,默认就是Void。有些时候需要提供一个自己的实现
UIView vs VC
上面这个例子有点不明白的是:MKMapView是一个UIView,并不是一个VC,为什么居然可以响应用户输入?
答案: UIView当然可以响应输入,不如UIButton,一般的UIView可以通过,
- gestureRecognizerShouldBegin
- touchesBegan/touchMoved/touchEnded
来响应处理消息。UIView/UIViewController 都是UIResponder的派生类,在消息链上其实是一样的。我们通常会把整个page当作一个UIController+他的view,但是一个UIViewController本身是可以包含子的UIViewController。
iOS的UIView/UIViewController并不完全是MVC的View和Controller。我们把一个Button的处理事件放在其VC里面,只是一种习惯。VC的另一个很重要的作用是Navigation,这是View不具备的。