五、SwiftUI的页面跳转/数据传递

709 阅读2分钟

1、NavigationLink方式跳转

image.png

image.png

import SwiftUI

struct InfoModel : Hashable
{
    var description : String
    var pictureName : String
}

struct DetailView : View
{
    var imageName : String
    var body: some View
    {
        Image(imageName)
    }
}

struct ContentView : View
{
    var messages : [InfoModel]
    
    var body: some View
    {
        NavigationView
        {
            List
            {
                ForEach(messages, id: \.self)
                { message in
                    NavigationLink(destination:
                    DetailView(imageName: message.pictureName))
                    {
                        Text(message.description)
                    }
                }
            }
            .navigationBarTitle("Pictures List")
        }
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider
{
    static var previews: some View
    {
        let model1 = InfoModel(description: "A lady with a horse", pictureName: "Picture1")
        let model2 = InfoModel(description: "An African animal with a very long neck", pictureName: "Picture2")
        
        return ContentView(messages: [model1, model2])
    }
}
#endif

2、导航栏设置

1. displayMode -- .inline 和 .large

image.png

image.png

2.navigationBarHidden(true) -- 隐藏导航栏

import SwiftUI

struct ContentView : View
{
    init() {
            //Use this if NavigationBarTitle is with Large Font
            UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor: UIColor.red]
        UINavigationBar.appearance().backgroundColor = .purple

            //Use this if NavigationBarTitle is with displayMode = .inline
            UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.red]
        }

    
    var body: some View
    {
        NavigationView
        {
            Text("SwiftUI's NavigationView")
//                .navigationBarTitle(Text("SwiftUI"))
//                .navigationBarTitle(Text("SwiftUI"), displayMode: .inline)
                .navigationBarTitle(Text("SwiftUI"), displayMode: .large)
//                .navigationBarHidden(true)
        }
    }
}

3、导航栏按钮设置 -- navigationBarItems(leading:,trailing:)

image.png

import SwiftUI

struct TrailingButtons : View
{
    var body: some View
    {
        HStack
        {
            Button(action: {
                print("Download the data")
            }) {
                Image(systemName: "icloud.and.arrow.down.fill")
            }
            
            Button(action: {
                print("Edit the data")
            }) {
                Image(systemName: "pencil.tip.crop.circle")
            }
        }
    }
}

struct ContentView : View
{
    var body: some View
    {
        NavigationView
        {
            Text("SwiftUI's NavigationView")
                .navigationBarTitle(Text("SwiftUI"))
                .navigationBarItems(leading:  Button(action: {
                   print("Go to index page")
                }) {
                   Text("Index")
                }, trailing: TrailingButtons())
        }
    }
}

4、导航页面添加工具栏 -- ToolbarItem(placement: ToolbarItemPlacement.bottomBar)

image.png

import SwiftUI

struct ToolbarButtons : View
{
    @State var isPresented = false
    
    var body: some View
    {
        HStack(spacing:50)
        {
            Button(action: {
                self.isPresented.toggle()
            }, label: {
                Image(systemName: "plus.circle")
            })
            .alert(isPresented: $isPresented, content: {
                Alert(title: Text("Operation"),
                      message: Text("Create a new file."))
            })
            
            Button(action: {}, label: {
                Image(systemName: "square.and.pencil")
            })
            
            Button(action: {}, label: {
                Image(systemName:"trash")
            })
        }
    }
}

struct ContentView : View
{
    var body: some View
    {
        NavigationView
        {
            Text("SwiftUI's NavigationView")
                .navigationTitle(Text("SwiftUI"))
                .toolbar(content:
                {
                    ToolbarItem(placement: ToolbarItemPlacement.bottomBar)
                    {
                        ToolbarButtons()
                    }
                })
        }
    }
}

4、PresentationMode实现导航的后退

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

image.png image.png image.png image.png

5、模态窗口的开关控制 -- @Binding

import SwiftUI

struct ContentView : View
{
    @State var isPresented = false
    
    var body: some View
    {
        PopView(show: $isPresented)
            .sheet(isPresented: $isPresented, content:
            {
                Button(action:
                {
                    self.isPresented.toggle()
                }){
                    Text("Dismiss").font(.largeTitle)
                }
            })
    }
}

struct PopView: View
{
    @Binding var show : Bool
    
    var body: some View
    {
        Button(action:
        {
            self.show.toggle()
        }) {
            Image(systemName: "person.crop.circle")
                .font(.system(size: 32))
                .frame(width: 64, height: 64)
                .foregroundColor(.black)
                .background(Color.white)
                .cornerRadius(30)
                .shadow(color: .orange, radius: 10, x: 0, y: 10)
        }
    }
}

6、@ObservableObject 监听实例对象

示例代码一:

import SwiftUI

class UserModel: ObservableObject
{
    @Published var nickName: String = ""
}

struct ContentView : View
{
    @ObservedObject var model = UserModel()
    @State var isPresented = false

    let dismiss = Alert.Button.default(Text("OK")) {}
    var alert: Alert
    {
        Alert(title: Text("Your nickname"),
             message: Text("\(self.model.nickName)"),
             dismissButton: dismiss)
    }
    
    var body: some View
    {
        VStack
        {
            TextField("Your nickname", text: $model.nickName)
                .padding()
            
            Button(action:
            {
                self.isPresented = true
            }) {
                Text("Show")
            }
            .alert(isPresented: $isPresented)
            { () -> Alert in
                alert
            }
        }
    }
}

示例代码二:

image.png

import SwiftUI

struct ContentView : View
{
    @ObservedObject var timer = MyTimer()
    
    var body: some View
    {
        Text("Coin number: \(self.timer.coinNumber)")
            .font(.largeTitle)
    }
}

7、@ObservedObject 和 @StateObject 的区别

a. model属性被@ObservedObject修饰时,它使model和视图之间处于订阅关系,当视图的状态发生变化时,会重新生成视图中所有的子视图,蛋挞数量从2->0。 b. 被@StateObject修饰时,会保持新的数据,蛋挞数量不会变化。

image.png image.png

import SwiftUI

class EggTartModel: ObservableObject
{
    @Published var score: Int = 0
    
    init()
    {
        print("EggTartModel Created.")
    }
}

struct FoodPicker: View
{
    @StateObject var model = EggTartModel()

    var body: some View
    {
        HStack
        {
            Text("Egg tart: \(model.score)")
            
            Button(" + ")
            {
                model.score += 1
            }
        }
        .padding()
    }
}

struct ContentView: View
{
    @State private var withCoupon = false
    
    var body: some View
    {
        VStack
        {
            FoodPicker()
            
            HStack
            {
                Text("Using Coupon: \(withCoupon ? "Yes" : "No")")
                Button("Coupon")
                {
                    withCoupon.toggle()
                }
            }
        }
    }
}

8、@EnvironmentObject

使用@EnvironmentObject,你可以在整个程序的任意页面设置和读取对象。

import SwiftUI

@main
struct DemoProjectApp: App
{
    var model : UserModel{
        let model = UserModel()
        model.nickName = "Super man"
        return model
    }
    
    var body: some Scene
    {
        WindowGroup
        {
            ContentView().environmentObject(model)
        }
    }
}
import SwiftUI

class UserModel: ObservableObject
{
    @Published var nickName: String = ""
}

struct DetailView: View
{
    @EnvironmentObject var model : UserModel
    
    var body: some View
    {
        Text("Your nickname: \(model.nickName)")
    }
    
    init()
    {
        print(model.nickName)
    }
}

struct ContentView : View
{
    @EnvironmentObject var model : UserModel
    @State var isPresented = false
    
    var body: some View
    {
        NavigationView
        {
            VStack
            {
                TextField("Your nickname", text: $model.nickName)
                .padding()
                
                NavigationLink(destination: DetailView())
                {
                    Text("Show Detail")
                }
            }
        }
    }
}

9、@Environment

@Environment需要根据预定的键,获取对应的值。

  1. 要使用@Environment,首先要定于一个遵循EnvironmentKey的Struct,并实现协议中的defaultValue属性,给它指定一个默认值。
  2. 如果需要添加更多的key,需要对EnvironmentValues进行扩展。添加新的键,如代码中的myCustomValue,实现myCustomValue的get和set方法。
  3. @Environment(.myCustomValue) var customValue: String 这句代码表示往myCustomValue中存储、读取数据,读取的结果放在customValue属性中。
  4. 可以在 ContentView_Previews 中通过 .environment(.myCustomValue, "You get another value from environment.") 给指定的键设置值。
import SwiftUI

private struct MyEnvironmentKey: EnvironmentKey
{
    static let defaultValue: String = "Default value"
}

extension EnvironmentValues
{
    var myCustomValue: String
    {
        get { self[MyEnvironmentKey.self] }
        set { self[MyEnvironmentKey.self] = newValue }
    }
}

struct ContentView: View
{
    @Environment(\.myCustomValue) var customValue: String
    
    var body: some View
    {
        VStack
        {
            Text(customValue)
        }
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider
{
    static var previews: some View
    {
        ContentView()
            .environment(\.myCustomValue, "You get another value from environment.")
    }
}
#endif

10、@AppStorage -- 将属性值同步到UserDefaults中

import SwiftUI

struct ContentView : View
{
    @AppStorage("nickname") var nickname: String = "Fate"
    
    var body: some View
    {
        VStack(spacing:20)
        {
            Text(nickname)
            
            Button(action: {
                self.nickname = "Finney"
                print(UserDefaults.standard.value(forKey: "nickname") as! String)
            }, label: {
                Text("Change nickname")
            })
        }
        .padding()
    }
}

11、SwiftUI生命周期管理 -- @Environment(.scenePhase) var swiftUIScenePhase

image.png

import SwiftUI

@main
struct DemoProjectApp: App
{
    @Environment(\.scenePhase) var swiftUIScenePhase
        
    var body: some Scene
    {
        WindowGroup
        {
            ContentView()
        }
        .onChange(of: swiftUIScenePhase) { scenePhase in
            print("Scene phase: \(scenePhase)")
        }
    }
}

12、AppDelegate

import UIKit

class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject
{
    @Published var globalVariable: String = ""
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
    {
        self.globalVariable = "twitter api key"
        return true
    }
}
import SwiftUI

struct ContentView: View
{
    @EnvironmentObject var appDelegate: AppDelegate

    var body: some View
    {
       Text("Value: \(appDelegate.globalVariable)")
    }
}

struct ContentView_Previews: PreviewProvider
{
    static var previews: some View
    {
        ContentView()
    }
}