The Util Designer
SwiftUI 模仿Photos App
xcode 13.4.1, swift 5.5, iOS 15.4
2022-08-20
這篇就講講如何用自定議畫面的跳轉來模仿Photos App。
1. 首先做一個初始畫面,照片的列表。
import SwiftUI

struct ContentView : View {
    @State var photoNames = ["cat1", "cat2", "cat3", "cat4", "cat5"]
    @State var selectedPhotoName = "cat2"
    @State var isSelected = false
    var body: some View {
        HStack {
            if !isSelected {
                VStack {
                    HStack {
                        ForEach(photoNames, id:\.self) { name in
                            Image(name)
                                .resizable()
                                .scaledToFit()
                        }
                    }
                    Spacer()
                }
            } else {
                
            }
        }
    }
}
2. 然後做一個選了一個照片後,放大圖片的頁面。
import SwiftUI

struct ContentView : View {
    @State var photoNames = ["cat1", "cat2", "cat3", "cat4", "cat5"]
    @State var selectedPhotoName = "cat2"
    @State var isSelected = true
    var body: some View {
        VStack {
            if !isSelected {
                HStack {
                    ForEach(photoNames, id:\.self) { name in
                        Image(name)
                            .resizable()
                            .scaledToFit()
                    }
                }
                Spacer()
            } else {
                Image(selectedPhotoName)
                    .resizable()
                    .scaledToFit()
            }
        }
    }
}
3. 只要改變isSelected就切換圖片列表和放大圖片。
import SwiftUI

struct ContentView : View {
    @State var photoNames = ["cat1", "cat2", "cat3", "cat4", "cat5"]
    @State var selectedPhotoName = "cat2"
    @State var isSelected = false
    var body: some View {
        VStack {
            if !isSelected {
                HStack {
                    ForEach(photoNames, id:\.self) { name in
                        Image(name)
                            .resizable()
                            .scaledToFit()
                            .onTapGesture {
                                withAnimation {
                                    isSelected.toggle()
                                }
                            }
                    }
                }
                Spacer()
            } else {
                Image(selectedPhotoName)
                    .resizable()
                    .scaledToFit()
                    .onTapGesture {
                        withAnimation {
                            isSelected.toggle()
                        }
                    }
            }
        }
    }
}
4. 為了顯示放大點選的圖片,用selectedPhotoName來記住點選了哪一個圖片的名字。
import SwiftUI

struct ContentView : View {
    @State var photoNames = ["cat1", "cat2", "cat3", "cat4", "cat5"]
    @State var selectedPhotoName = "cat2"
    @State var isSelected = false
    var body: some View {
        VStack {
            if !isSelected {
                HStack {
                    ForEach(photoNames, id:\.self) { name in
                        Image(name)
                            .resizable()
                            .scaledToFit()
                            .onTapGesture {
                                selectedPhotoName = name
                                withAnimation {
                                    isSelected.toggle()
                                }
                            }
                    }
                }
                Spacer()
            } else {
                Image(selectedPhotoName)
                    .resizable()
                    .scaledToFit()
                    .onTapGesture {
                        withAnimation {
                            isSelected.toggle()
                        }
                    }
            }
        }
    }
}
5. 最後使用SwiftUI提供了一個方法@Namespace和matchedGeometryEffect來做到切換頁面時動畫:
import SwiftUI

struct ContentView : View {
    @Namespace var namespace
    @State var photoNames = ["cat1", "cat2", "cat3", "cat4", "cat5"]
    @State var selectedPhotoName = "cat2"
    @State var isSelected = false
    var body: some View {
        VStack {
            if !isSelected {
                HStack {
                    ForEach(photoNames, id:\.self) { name in
                        Image(name)
                            .resizable()
                            .scaledToFit()
                            .matchedGeometryEffect(id: name, in: namespace)
                            .onTapGesture {
                                selectedPhotoName = name
                                withAnimation {
                                    isSelected.toggle()
                                }
                            }
                    }
                }
                Spacer()
            } else {
                Image(selectedPhotoName)
                    .resizable()
                    .scaledToFit()
                    .matchedGeometryEffect(id: selectedPhotoName, in: namespace)
                    .onTapGesture {
                        withAnimation {
                            isSelected.toggle()
                        }
                    }
            }
        }
    }
}