代码之家  ›  专栏  ›  技术社区  ›  Robin

LazyVGrid项目预计不会击中测试区域

  •  0
  • Robin  · 技术社区  · 4 月前

    我正在学习SwiftUI,并试图使用LazyVGrid实现一个照片库应用程序。 在ContentView的body()方法的ForEach循环中,我有一个从库中提取的每张照片的缩略图视图。

    import SwiftUI
    import Photos
    import CoreImage
    import ImageIO
    import AVKit
    import AVFoundation
    
    struct Content: Identifiable {
        let id = UUID()
        let phAsset: PHAsset
        let index: Int
    }
    
    struct ContentView: View {
        @State private var contents: [Content] = []
        @State private var selectedContent: Content? = nil
        @State private var isLoading: Bool = true
    
        var body: some View {
            NavigationView {
                let minItemWidth: CGFloat = 100
                let spacing: CGFloat = 2
                let screenWidth = UIScreen.main.bounds.width
                let columns = Int((screenWidth + spacing) / (minItemWidth + spacing))
                let totalSpacing = spacing * CGFloat(columns - 1)
                let itemWidth = (screenWidth - totalSpacing) / CGFloat(columns)
                ZStack {
                    ScrollView {
                        LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 2) {
                            ForEach(contents) { content in
                                ThumbnailView(content: content.phAsset)
                                    .frame(width: itemWidth, height: itemWidth)
                                    .onTapGesture {
                                        selectedContent = content
                                    }
                                    .padding(4)
                                    .background(Color.blue.opacity(0.5))
                                    .clipped()
                            }
                        }
                        .padding(2)
                    }
                    .navigationTitle("Photos Library")
                    .onAppear(perform: loadContents)
                    .sheet(item: $selectedContent) { content in
                        NavigationView {
                            DetailImageView(asset: content.phAsset)
                                .navigationBarItems(trailing: Button("Done") {
                                    selectedContent = nil
                                })
                        }
                    }
    
                    if isLoading {
                        ProgressView("Loading...")
                            .progressViewStyle(CircularProgressViewStyle())
                            .scaleEffect(1.5, anchor: .center)
                    }
                }
            }
        }
    
        func loadContents() {
            PHPhotoLibrary.requestAuthorization { status in
                if status == .authorized {
                    fetchContents()
                }
            }
        }
    
        func fetchContents() {
            let fetchOptions = PHFetchOptions()
    
            DispatchQueue.global(qos: .background).async {
                self.isLoading = true
                let fetchResult = PHAsset.fetchAssets(with: fetchOptions)
                var fetchedContents: [Content] = []
                var index = 0
                fetchResult.enumerateObjects { (phAsset, _, _) in
                    fetchedContents.append(Content(phAsset: phAsset, index: index))
                    index += 1
                }
                self.isLoading = false
    
                DispatchQueue.main.async {
                    contents = fetchedContents
                }
            }
        }
    }
    
    struct ThumbnailView: View {
        let content: PHAsset
        @State private var image: UIImage? = nil
    
        var body: some View {
            Group {
                if let image = image {
                    Image(uiImage: image)
                        .resizable()
                        .scaledToFill()
                } else {
                    Color.gray
                }
            }
            .onAppear(perform: loadImage)
        }
    
        func loadImage() {
            let imageManager = PHImageManager.default()
            let requestOptions = PHImageRequestOptions()
            requestOptions.isSynchronous = false
            imageManager.requestImage(for: content, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions) { (image, _) in
                self.image = image
            }
        }
    }
    
    struct DetailImageView: View {
        let asset: PHAsset
        @State private var image: UIImage?
    
        var body: some View {
            Group {
                if let image = image {
                    Image(uiImage: image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .edgesIgnoringSafeArea(.all)
                } else {
                    ProgressView()
                }
            }
            .onAppear(perform: loadFullImage)
        }
    
        func loadFullImage() {
            let manager = PHImageManager.default()
            let options = PHImageRequestOptions()
            options.deliveryMode = .highQualityFormat
            options.isNetworkAccessAllowed = true
    
            manager.requestImage(
                for: asset,
                targetSize: PHImageManagerMaximumSize,
                contentMode: .aspectFit,
                options: options
            ) { result, _ in
                image = result
            }
        }
    }
    

    问题是,当我点击某个项目的某个区域时,打开的项目不是预期的。我试着调试这个问题一整天,发现如果我在ThumbnailView上注释掉“.clipped()”修饰符,我可以看到LazyVGrid的项目视图是重叠的,这就是为什么打开顶部视图而不是我以为我点击的视图的原因。这对我来说很奇怪,因为我相信如果我用.frame()修饰符指定视图的大小,它应该无法接收区域外的任何触摸事件。

    我是SwiftUI和iOS编程的新手,非常感谢您的帮助。

    1 回复  |  直到 4 月前
        1
  •  1
  •   Benzy Neez    4 月前

    当图像被缩放以填充时,即使被剪切,溢出仍然可以接受点击。

    要修复,请尝试应用 .contentShape 紧接着 .frame 改性剂。这需要在 .onTapGesture 修改器:

    ThumbnailView(content: content.phAsset)
        .frame(width: itemWidth, height: itemWidth)
        .contentShape(Rectangle()) // 👈 here
        .onTapGesture {
            selectedContent = content
        }
        // + other modifiers