Closed28

学習メモ:Introduction to SwiftUI - WWDC 2020 - Videos - Apple Developer

yorifujiyorifuji

プレビュー画面でcommand+optionでクリックすると同じメニューが開く

yorifujiyorifuji
            List(sandwiches) { sandwich in
                SandwichCell(sandwich: sandwich)
            }

            List {
                ForEach(sandwiches) { sandwich in
                    SandwichCell(sandwich: sandwich)
                }
                HStack {
                    Spacer()
                    Text("\(sandwiches.count) sandwitches")
                        .foregroundColor(.secondary)
                    Spacer()
                }
            }

に変える

yorifujiyorifuji

PreviewProviderにNavigationViewを追加するとプレビューでNavigationViewの動作が確認できる

struct SandwichDetail: View {
    var sandwich: Sandwich
    var body: some View {
        Image(systemName: "camera")
            .resizable()
            .aspectRatio(contentMode: .fit)
            .navigationTitle(sandwich.name)
    }
}

struct SandwichDetail_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            SandwichDetail(sandwich: testData[0])
        }
    }
}
yorifujiyorifuji

tapで画像のzoomを切り替え

struct SandwichDetail: View {
    var sandwich: Sandwich
    @State private var zoomed = false
    var body: some View {
        Image(systemName: "camera")
            .resizable()
            .aspectRatio(contentMode: zoomed ? .fill : .fit)
            .onTapGesture {
                zoomed.toggle()
            }
            .navigationTitle(sandwich.name)
    }
}
yorifujiyorifuji

画像をセーフエリアも含めて画面全体に拡大したいときはモディフィアを追加する

       .edgesIgnoringSafeArea(.bottom)
yorifujiyorifuji

タップした時の動作にアニメーションを加える

            .onTapGesture {
                withAnimation {
                    zoomed.toggle()
                }
            }
yorifujiyorifuji
    var body: some View {
        VStack {
            Spacer(minLength: 0)
            Image(systemName: "camera")
                .resizable()
                .aspectRatio(contentMode: zoomed ? .fill : .fit)
                .onTapGesture {
                    withAnimation {
                        zoomed.toggle()
                    }
                }
            Spacer(minLength: 0)
            HStack {
                Spacer()
                Label("Spicy", systemImage: "flame.fill")
                Spacer()
            }
            .padding(.all)
            .font(Font.headline.smallCaps())
            .background(Color.red)
            .foregroundColor(.white)
        }
        .edgesIgnoringSafeArea(.bottom)
        .navigationTitle(sandwich.name)
    }

yorifujiyorifuji

プレビューを複数表示して違いを確認する


struct SandwichDetail_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            NavigationView {
                SandwichDetail(sandwich: testData[0])
            }
            NavigationView {
                SandwichDetail(sandwich: testData[1])
            }
        }
    }
}

yorifujiyorifuji

トランジションを追加

            if sandwich.isSpicy && !zoomed {
                HStack {
                    Spacer()
                    Label("Spicy", systemImage: "flame.fill")
                    Spacer()
                }
                .padding(.all)
                .font(Font.headline.smallCaps())
                .background(Color.red)
                .foregroundColor(.white)
                .transition(.move(edge: .bottom))
            }
yorifujiyorifuji

ターゲットを(シミュレータの)iPadに切り替えるとプレビューがiPadに切り替わる

yorifujiyorifuji

ターゲットをmacに変更するとmacアプリとして表示

yorifujiyorifuji

新しいモデルを用意

class SandwichStore: ObservableObject {
    @Published var sandwiches: [Sandwich]
    init(sandwiches: [Sandwich] = []) {
        self.sandwiches = sandwiches
    }
}

let testStore = SandwichStore(sandwiches: testData)

  • ObservableObject
  • @Published
yorifujiyorifuji

App本体にStoreを追加

@main
struct wwdc20_swiftui_introductionApp: App {
    @StateObject private var store = SandwichStore()
    var body: some Scene {
        WindowGroup {
            ContentView(store: store)
        }
    }
}
yorifujiyorifuji

ContentViewを変更

struct ContentView: View {
    @ObservedObject var store: SandwichStore

    var body: some View {
        NavigationView {
            List {
                ForEach(store.sandwiches) { sandwich in
                    SandwichCell(sandwich: sandwich)
                }
                HStack {
                    Spacer()
                    Text("\(store.sandwiches.count) sandwitches")
                        .foregroundColor(.secondary)
                    Spacer()
                }
            }
            .navigationTitle("Sandwiches")

            Text("Select a sandwich")
                .font(.title)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(store: testStore)
    }
}
  • @ObservedObject var store: SandwichStore
yorifujiyorifuji

リストの編集に対応したコードスニペット

    func makeSandwich() {
        withAnimation {
            store.sandwiches.append(Sandwich(name: "new sandwich", isSpicy: true))
        }
    }

    func moveSandwiches(from: IndexSet, to: Int) {
        withAnimation {
            store.sandwiches.move(fromOffsets: from, toOffset: to)
        }
    }

    func deleteSandwiches(offsets: IndexSet) {
        withAnimation {
            store.sandwiches.remove(atOffsets: offsets)
        }
    }

yorifujiyorifuji

iOSだけToolBarにボタンを表示する

            .toolbar {
                #if os(iOS)
                    EditButton()
                #endif
            }
yorifujiyorifuji

Addボタンを追加したけど表示されない

            .toolbar {
                #if os(iOS)
                EditButton()
                #endif
                Button("Add", action: makeSandwich)
            }
yorifujiyorifuji

wwdcのビデオとzennをサイドバイサイドに並べて作業しました。

yorifujiyorifuji

感想

  • 実機を使用しないでプレビューだけでアプリを組み立てられるので便利
  • XcodeのGUIでリファクタリングやコンポーネントの変更ができる
このスクラップは2020/11/30にクローズされました