Hi, 大家好,我是姜友华。这两天在适应SwiftUI,SwiftUI较Swift UIKit在构建APP有较大的改变。在这里,我将其中的注意点记录下来。
通常我在学习新的界面类开发框架时,回从以下几个方面着手:页面的构建、页面的跳转、状态的改变、数据的传递及数据的持久化。SwiftUI较Swift UIKit在数据的传递与数据的持久化没有变化,所以这里只说说:页面的构建、页面的跳转、数据的传递。
一、页面的构建。
- View:
- Text、
- Images
- Buttons
- Controls
- Value Selectors
- Value Indicators
...
2.Layout
- Stack
- Grids
- Containers
- Scroll Views
- List
- Tables
- Spacers and Dividers
...
二、页面的跳转
- 判断渲染
if isShow {
MyView()
}
- Present
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
backFunc()
} content: {
MyView()
}
}
}
- Navigation
- NavigationLink、NavigationView
NavigationView {
List {
NavigationLink("Purple", destination: ColorDetail(color: .purple))
NavigationLink("Pink", destination: ColorDetail(color: .pink))
NavigationLink("Orange", destination: ColorDetail(color: .orange))
}
.navigationTitle("Colors")
Text("Select a Color") // A placeholder to show before selection.
}
struct ColorDetail: View {
var color: Color
var body: some View {
color
.frame(width: 200, height: 200)
.navigationTitle(color.description.capitalized)
}
}
- TabBar
TabView {
Text("The First Tab")
.badge(10)
.tabItem {
Image(systemName: "1.square.fill")
Text("First")
}
Text("Another Tab")
.tabItem {
Image(systemName: "2.square.fill")
Text("Second")
}
Text("The Last Tab")
.tabItem {
Image(systemName: "3.square.fill")
Text("Third")
}
}
.font(.headline)
三、状态的改变
- @State、@Binding, struct;@StateObject、@Binding Object、@ObservableObject、@EnvironmentObject,class。
- @State
@State private var isVisible = true
......
if isVisible == true {
Text("Hello") // Only rendered when isVisible is true.
}
- @Binding 作用于父子视图。
// 子视图
struct PlayButton: View {
@Binding var isPlaying: Bool
var body: some View {
Button(action: {
self.isPlaying.toggle()
}) {
Image(systemName: isPlaying ? "pause.circle" : "play.circle")
}
}
}
// 父视图
struct PlayerView: View {
var episode: Episode
@State private var isPlaying: Bool = false
var body: some View {
VStack {
Text(episode.title)
Text(episode.showTitle)
PlayButton(isPlaying: $isPlaying)
}
}
}
- @ObservableObject 即KVO模式。
class Contact: ObservableObject {
@Published var name: String
@Published var age: Int
init(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())
// Prints "24 will change"
// Prints "25"
objectWillChange是ObservableObject协议的方法。
- EnvironmentObject
class User: ObservableObject {
@Publish var name: String
@Publish var age: Int
}
struct EditView: View {
@EnvironmentObject var user: User
var body: some View {
TextField("Name", text: $user.name)
}
}
struct DisplayView: View {
@EnvironmentObject var user: User
var body: some View {
Text(user.name)
}
}
struct ContentView: View {
let user = User(name: "Jiang", age)
var body: some View {
VStack {
EditView()
DisplayView()\
}.environmentObject(user)
}
}
2 @Environment、@ FocusState
- @Environment
获取环境变量。
@Environment(\.colorScheme) var colorScheme: ColorScheme
......
if colorScheme == .dark { // Checks the wrapped value.
DarkContent()
} else {
LightContent()
}
- @FocusState
becomefirstresponder
struct LoginForm {
enum Field: Hashable {
case username
case password
}
@State private var username = ""
@State private var password = ""
@FocusState private var focusedField: Field?
var body: some View {
Form {
TextField("Username", text: $username)
.focused($focusedField, equals: .username)
SecureField("Password", text: $password)
.focused($focusedField, equals: .password)
Button("Sign In") {
if username.isEmpty {
focusedField = .username
} else if password.isEmpty {
focusedField = .password
} else {
handleLogin(username, password)
}
}
}
}
}
四、GeometryReader与Path
- GeometryReader
- 获取屏幕信息。
GeometryReader { geometry in
HStack(spacing: 0) {
Text("Left")
.font(.largeTitle)
.foregroundColor(.black)
.frame(width: geometry.size.width * 0.33)
.background(Color.yellow)
Text("Right")
.font(.largeTitle)
.foregroundColor(.black)
.frame(width: geometry.size.width * 0.67)
.background(Color.orange)
}
}
.frame(height: 50)
- GeometryReader与Path
var body: some View {
GeometryReader{ geometry in
Path{ path in
path.moveto()
path.addLine()
}
}
}
- Shape
struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
return path
}
}
五、SwiftUI与UIKit组件互用。
- UIKit用SwiftUI
let swiftUIView = Text("Jiang youhua")
let viewCtrl = UIHostingController(rootView: swiftUIView)
- UIView转SwiftUI
struct TextView: UIViewRepresentable {
@Binding var text: NSMutableAttributedString
func makeUIView(context: Context) -> UITextView {
UITextView()
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.attributedText = text
}
}
- UIViewController转SwiftUI
struct PageViewController<Page: View>: UIViewControllerRepresentable {
var pages: [Page]
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
pageViewController.setViewControllers(
[context.coordinator.controllers[0]], direction: .forward, animated: true)
}
class Coordinator: NSObject, UIPageViewControllerDataSource {
var parent: PageViewController
var controllers = [UIViewController]()
init(_ pageViewController: PageViewController) {
parent = pageViewController
controllers = parent.pages.map { UIHostingController(rootView: $0) }
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController?
{
guard let index = controllers.firstIndex(of: viewController) else {
return nil
}
if index == 0 {
return controllers.last
}
return controllers[index - 1]
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerAfter viewController: UIViewController) -> UIViewController?
{
guard let index = controllers.firstIndex(of: viewController) else {
return nil
}
if index + 1 == controllers.count {
return controllers.first
}
return controllers[index + 1]
}
}
}
好,我是姜友华,今天就到这里,下次见。