SwiftUI基礎教程(2) 第二章:SwiftUI基礎元素實例

本文是SwiftUI基礎教程基礎教程的第二部分,第一部分參見SwiftUI基礎教程(1)

第二章:SwiftUI基礎元素實例

2.11 導航

// 以下代碼效果圖如,navi所示
struct SwiftUINavi: View {
    var tankInfo = [
        ANewTankInfo(name: "M1A2", image: "1"),
        ANewTankInfo(name: "T90", image: "2"),
        ANewTankInfo(name: "T72", image: "3"),
        ANewTankInfo(name: "99A", image: "4"),
        ANewTankInfo(name: "LieKeLieEr", image: "5"),
        ANewTankInfo(name: "BaoEr", image: "6"),
        ANewTankInfo(name: "09", image: "7"),
        ANewTankInfo(name: "10", image: "8"),
    ]
    
    init() {
        let navBarAppearance = UINavigationBarAppearance()
        navBarAppearance.titleTextAttributes = [.foregroundColor:UIColor.systemRed, .font: UIFont(name: "ArialRoundedMTBold", size: 20)]
        navBarAppearance.largeTitleTextAttributes =  [.foregroundColor:UIColor.systemBlue, .font: UIFont(name: "ArialRoundedMTBold", size: 40)]
        navBarAppearance.setBackIndicatorImage(UIImage(systemName: "arrow.turn.up.left"), transitionMaskImage: UIImage(systemName: "arrow.turn.up.left"))
        
        UINavigationBar.appearance().standardAppearance = navBarAppearance;
        UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance;
        UINavigationBar.appearance().compactAppearance = navBarAppearance;
        UINavigationBar.appearance().tintColor = .purple
    }
    
    var body: some View {
        NavigationView {
            List(tankInfo) { aTankInfo in
                NavigationLink(destination: TankDetailInfo(tankInfo: aTankInfo)) {
                    NewBasicRowView(aTankInfo: aTankInfo)
                }
            }
            .navigationBarTitle("坦克大賞", displayMode: .automatic)
        }
    }
}


struct SwiftUINavi_Previews: PreviewProvider {
    static var previews: some View {
        if #available(iOS 15.0, *) {
            SwiftUINavi()
                .previewInterfaceOrientation(.portrait)
        } else {
            // Fallback on earlier versions
        }
    }
}


struct NewBasicRowView: View {
    var aTankInfo: ANewTankInfo
    
    var body: some View {
        HStack {
            Image(aTankInfo.image)
                .resizable()
                .frame(width: 40, height: 40)
                .cornerRadius(5)
            
            Text(aTankInfo.name)
                .font(.system(size: 15))
                .foregroundColor(.green)
        }
    }
}

struct ANewTankInfo: Identifiable {
    let id = UUID()
    var name: String
    var image: String
}

struct TankDetailInfo: View {
    // @Environment 後面的內容會講到
    @Environment(\.presentationMode) var mode
    
    var tankInfo: ANewTankInfo
    
    var body: some View {
        VStack {
            Image(tankInfo.image)
                .resizable()
                .aspectRatio(contentMode: .fit)
            
            Text(tankInfo.name)
                .font(.system(.title, design: .rounded))
                .fontWeight(.bold)
            
            Spacer()
        }
        .navigationBarTitle("", displayMode: .inline)
        .navigationBarBackButtonHidden(true)
        .navigationBarItems(leading: Button(action: {
            self.mode.wrappedValue.dismiss()
        }, label:{
            Image(systemName: "chevron.left.circle.fill").font(.largeTitle)
                .foregroundColor(.red)
            })
        )
    }
}

2.12 模態視圖

struct SwiftUIModality: View {
    var tankInfos = [
        ATankInfoModaly(name: "M1A2", image: "1"),
        ATankInfoModaly(name: "T90", image: "2"),
        ATankInfoModaly(name: "T72", image: "3"),
        ATankInfoModaly(name: "99A", image: "4"),
        ATankInfoModaly(name: "LieKeLieEr", image: "5"),
        ATankInfoModaly(name: "BaoEr", image: "6"),
        ATankInfoModaly(name: "09", image: "7"),
        ATankInfoModaly(name: "10", image: "8"),
    ]
    
    // 目前有個疑問:把SwiftUIModality中目前起作用的.sheet語句註釋掉,把現在已註釋的其他語句放開,則無法其作用,不知道爲什麼
    // 有高手知道的話,辛苦告知
    
    // @State private  var show = false
    
    @State var selectTankInfo: ATankInfoModaly?
    
    var body: some View {
        NavigationView {
            List(tankInfos) { aTankInfo in
                BigImageRowModaly(aTankInfo: aTankInfo)
                    .onTapGesture {
                        self.selectTankInfo = aTankInfo
//                        self.show = true
                    }
                
            }
            .sheet(item:self.$selectTankInfo) { atankinfo in
                    DetailViewModaly(aTankInfo: atankinfo)

            }
            .navigationTitle("坦克大賞")
            
//            .sheet(isPresented: self.$show) {
//                if let tankInfo = self.$selectTankInfo {
//                    DetailViewModaly(aTankInfo: tankInfo!)
//                }
//            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

struct SwiftUIModality_Previews: PreviewProvider {
    static var previews: some View {
        SwiftUIModality()
    }
}

struct ATankInfoModaly: Identifiable {
    let id = UUID()
    var name: String
    var image: String
}

struct DetailViewModaly: View {
    var aTankInfo: ATankInfoModaly
    @Environment(\.presentationMode) var presentationMode
    @State private var showAlert = false
    
    var body: some View {
        if #available(iOS 15.0, *) {
            VStack {
                Image(aTankInfo.image)
                    .resizable()
                    .scaledToFit()
                    .cornerRadius(5)
                
                Text(aTankInfo.name)
                    .font(.system(size: 15))
                    .foregroundColor(.green)
            }
            .edgesIgnoringSafeArea(.top)
            .overlay {
                HStack {
                    Spacer()
                    VStack {
                        Button {
                            self.showAlert = true
                            
                            // 請注意,爲何不是把alert寫在這裏!
                            // 目前我的理解是,寫在這裏的話,導致SwiftUI無法構造合適的View
                            /*
                             Alert(title: Text("提醒"), message: Text("確定要離開嗎"), primaryButton: .default(Text("確定"), action: {
                                                                                             self.presentationMode.wrappedValue.dismiss()
                             }           ), secondaryButton: .cancel(Text("關閉")))
                             */
                        } label: {
                            Image(systemName: "chevron.down.circle.fill")
                                .font(.largeTitle)
                                .foregroundColor(.white)
                        }
                        .padding(.trailing, 20)
                        .padding(.top, 40)
                        
                        Spacer()
                    }
                }
            }.alert(isPresented: self.$showAlert) {
                Alert(title: Text("提醒"), message: Text("確定要離開嗎"), primaryButton: .default(Text("確定"), action: {
                                                                                self.presentationMode.wrappedValue.dismiss()
                }           ), secondaryButton: .cancel(Text("關閉")))
            }
            
            
        } else {
            // Fallback on earlier versions
        }

    }
}

struct BigImageRowModaly : View {
    var aTankInfo: ATankInfoModaly
    
    var body: some View {
        VStack {
            Image(aTankInfo.image)
                .resizable()
                .scaledToFit()
                .cornerRadius(5)
            
            Text(aTankInfo.name)
                .font(.system(size: 15))
                .foregroundColor(.green)
            
            Spacer()
        }
    }
}

        上述代碼的實際效果如下圖所示。


2.13 表單

// 主文件
struct SwiftUIForm: View {
    @State var tankInfos = [
        ATankInfoForm(name: "M1A2", image: "1", type:"美國", phone: "119", priceLevel: 3, isFavorite: true, isCheckIn: false),
        ATankInfoForm(name: "T90", image: "2", type:"俄羅斯", phone: "119", priceLevel: 2, isFavorite: false, isCheckIn: true),
        ATankInfoForm(name: "T72", image: "3", type:"俄羅斯", phone: "119", priceLevel: 2, isFavorite: false, isCheckIn: true),
        ATankInfoForm(name: "99A", image: "4", type:"俄羅斯", phone: "119", priceLevel: 2, isFavorite: false, isCheckIn: true),
        ATankInfoForm(name: "LieKeLieEr", image: "5", type:"德國", phone: "123", priceLevel: 5, isFavorite: true, isCheckIn: true),
        ATankInfoForm(name: "BaoEr", image: "6", type:"德國", phone: "123", priceLevel: 2, isFavorite: false, isCheckIn: true),
        ATankInfoForm(name: "09", image: "7", type:"日版", phone: "777", priceLevel: 1, isFavorite: true, isCheckIn: true),
        ATankInfoForm(name: "10", image: "8", type:"日本", phone: "911", priceLevel: 4, isFavorite: false, isCheckIn: false),
    ]
        
    @State private var selectedRestaurant: ATankInfoForm?
    @State private var showSettings: Bool = false
    
    var body: some View {
        NavigationView {
            List {
                ForEach(tankInfos) { aTankInfo in
                    BasicImageRow(tankinfo: aTankInfo)
                        .contextMenu {
                            
                            Button(action: {
                                // mark the selected restaurant as check-in
                                self.checkIn(item: aTankInfo)
                            }) {
                                HStack {
                                    Text("Check-in")
                                    Image(systemName: "checkmark.seal.fill")
                                }
                            }
                            
                            Button(action: {
                                // delete the selected restaurant
                                self.delete(item: aTankInfo)
                            }) {
                                HStack {
                                    Text("Delete")
                                    Image(systemName: "trash")
                                }
                            }
                            
                            Button(action: {
                                // mark the selected restaurant as favorite
                                self.setFavorite(item: aTankInfo)
                                
                            }) {
                                HStack {
                                    Text("Favorite")
                                    Image(systemName: "star")
                                }
                            }
                        }
                        .onTapGesture {
                            self.selectedRestaurant = aTankInfo
                        }
                }
                .onDelete { (indexSet) in
                    self.tankInfos.remove(atOffsets: indexSet)
                }
            }
            
            .navigationBarTitle("坦克大賞")
            .navigationBarItems(trailing:
                                    
                                    Button(action: {
                self.showSettings = true
            }, label: {
                Image(systemName: "gear").font(.title)
                    .foregroundColor(.black)
            })
            )
            .sheet(isPresented: $showSettings) {
                SettingView()
            }
        }
    }
    
        func delete(item restaurant: ATankInfoForm) {
                if let index = self.tankInfos.firstIndex(where: { $0.id == restaurant.id }) {
                    self.tankInfos.remove(at: index)
                }
            }
            
         func setFavorite(item restaurant: ATankInfoForm) {
                if let index = self.tankInfos.firstIndex(where: { $0.id == restaurant.id }) {
                    self.tankInfos[index].isFavorite.toggle()
                }
            }
            
         func checkIn(item restaurant: ATankInfoForm) {
                if let index = self.tankInfos.firstIndex(where: { $0.id == restaurant.id }) {
                    self.tankInfos[index].isCheckIn.toggle()
                }
            }
}

struct SwiftUIForm_Previews: PreviewProvider {
    static var previews: some View {
        SwiftUIForm()
    }
}

struct ATankInfoForm: Identifiable {
    
    let id = UUID()
    var name: String
    var image: String
    var type: String
    var phone: String
    var priceLevel: Int
    var isFavorite: Bool = false
    var isCheckIn: Bool = false
}
    
    struct BasicImageRow: View {
        var tankinfo: ATankInfoForm
        
        var body: some View {
          
                HStack {
                    Image(tankinfo.image)
                        .resizable()
                        .frame(width: 60, height: 60)
                        .clipShape(Circle())
                        .padding(.trailing, 10)
                    
                    VStack(alignment: .leading) {
                        HStack {
                            Text(tankinfo.name)
                                .font(.system(.body, design: .rounded))
                                .bold()
                            
                            Text(String(repeating: "$", count: tankinfo.priceLevel))
                                .font(.subheadline)
                                .foregroundColor(.gray)

                        }
                        
                        Text(tankinfo.type)
                            .font(.system(.subheadline, design: .rounded))
                            .bold()
                            .foregroundColor(.secondary)
                            .lineLimit(3)
                        
                        Text(tankinfo.phone)
                            .font(.system(.subheadline, design: .rounded))
                            .foregroundColor(.secondary)
                    }
                    
                    Spacer()
                        .layoutPriority(-100)
                    
                    if tankinfo.isCheckIn {
                        Image(systemName: "checkmark.seal.fill")
                            .foregroundColor(.red)
                    }
                    
                    if tankinfo.isFavorite {
    //                    Spacer()
                        
                        Image(systemName: "star.fill")
                        .foregroundColor(.yellow)
                    }
                }
                
            
        }
    }

// 設置視圖;請注意:用戶選擇如何反映到UI上
struct SettingView: View {
    
    @Environment(\.presentationMode) var presentationMode
    
    private var displayOrders = [ "Alphabetical", "Show Favorite First", "Show Check-in First"]
    
    @State private var selectedOrder = 0
    @State private var showCheckInOnly = false
    @State private var maxPriceLevel = 5
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("SORT PREFERENCE")) {
                    Picker(selection: $selectedOrder, label: Text("Display order")) {
                        ForEach(0 ..< displayOrders.count, id: \.self) {
                            Text(self.displayOrders[$0])
                        }
                    }
                }
                
                Section(header: Text("FILTER PREFERENCE")) {
                    Toggle(isOn: $showCheckInOnly) {
                        Text("Show Check-in Only")
                    }
                    
                    Stepper(onIncrement: {
                        self.maxPriceLevel += 1
                        
                        if self.maxPriceLevel > 5 {
                            self.maxPriceLevel = 5
                        }
                    }, onDecrement: {
                        self.maxPriceLevel -= 1
                        
                        if self.maxPriceLevel < 1 {
                            self.maxPriceLevel = 1
                        }
                    }) {
                        Text("Show \(String(repeating: "$", count: maxPriceLevel)) or below")
                    }
                }
            }
            
            
            .navigationBarTitle("Settings")
            
            .navigationBarItems(leading:
                
                Button(action: {
                    self.presentationMode.wrappedValue.dismiss()
                }, label: {
                    Text("Cancel")
                        .foregroundColor(.black)
                })
                
                , trailing:

                Button(action: {
                    self.presentationMode.wrappedValue.dismiss()
                }, label: {
                    Text("Save")
                        .foregroundColor(.black)
                })
            )
            
        }
        
    }
}

struct SettingView_Previews: PreviewProvider {
    static var previews: some View {
        SettingView()
    }
}

        本小節所用到的UI組件比較多,但是和以前相似,沒有多大的難度,請大家最好自己動手敲一敲代碼,如果出現問題就自己解決。


2.14 Combine基本使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章