【问题标题】:How to render new instances of an object in SwiftUI如何在 SwiftUI 中渲染对象的新实例
【发布时间】:2021-08-17 03:42:42
【问题描述】:

我有一组图像,并将每个图像渲染为可以拖动的卡片对象。我不想渲染每一个,因为这会导致性能问题。目前我正在使用 forEach 循环来渲染所有这些,但是我将如何渲染说两个,直到一个被删除。一旦一个被删除,我会在当前可见的卡片下渲染一个新实例。我不需要这个确切的实现,但有人可以指出正确的方法。

import Photos
import SwiftUI

var imageToDelete = [PHAsset]()

struct ContentView: View {
    var body: some View {
        VStack{
            // Here is where I'm rendering the card
            ZStack{
                ForEach(imageObjectGroup){ card in
                    CardView(card: card).padding(8)
                }
            }.zIndex(1.0)
        }
    }
}

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

// You can ignore this I am converting the array item (PHAsset) to a UIImage so I can render it

func getAssetThumbnail(asset: PHAsset) -> UIImage {
    let manager = PHImageManager.default()
    let option = PHImageRequestOptions()
    var thumbnail = UIImage()
    option.isSynchronous = true
    manager.requestImage(for: asset, targetSize: CGSize(width: 350, height: 350), contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
        thumbnail = result!
    })
    return thumbnail
}

// Card Object
struct CardView: View{
    @State var card: ImageObject
    let cardGradient = Gradient(colors: [Color.black.opacity(0), Color.black.opacity(0.5)])
    var body: some View{
        ZStack(alignment: .leading){
            Image(uiImage: getAssetThumbnail(asset: card.image)).resizable().padding(8)
            LinearGradient(gradient: cardGradient, startPoint: .top, endPoint: .bottom)
            
            HStack {
                Image("yes").resizable().aspectRatio(contentMode: .fit).frame(width: 150).opacity(Double(card.x/10 - 1))
                Spacer()
                Image("nope").resizable().aspectRatio(contentMode: .fit).frame(width: 150).opacity(Double(card.x/10 * -1 - 1))
            }
        //ZStack follows the coordinates of the card model
        }.offset(x: card.x, y: card.y).rotationEffect(.init(degrees: card.degree)).gesture(
            //Gesture recognizer updates coordinate values
            DragGesture().onChanged { value in
                card.x = value.translation.width
                card.y = value.translation.height
                card.degree = 7 * (value.translation.width > 0 ? 1 : -1)
            }
            //When user stops dragging
            .onEnded { value in
                withAnimation(.interpolatingSpring(mass: 1.0, stiffness: 50, damping: 8, initialVelocity: 0)){
                        switch value.translation.width {
                            case 0...100:
                                card.x = 0; card.degree = 0; card.y = 0
                            // Keep
                            case let x where x > 100:
                                card.x = 200; card.degree = 12
                            case (-100)...(-1):
                                card.x = 0; card.degree = 0; card.y = 0;
                            // Delete
                            case let x where x < -100:
                                card.x = -200; card.degree = -12
                                print(card.image)
                                imageToDelete.append(card.image)
                                PHPhotoLibrary.shared().performChanges( {
                                PHAssetChangeRequest.deleteAssets(imageToDelete as NSFastEnumeration)},
                                completionHandler: {
                                    success, error in NSLog("Deletion prompt complete")
                                })
                            default: card.x = 0; card.y = 0
                        }
                }
            }
        ).cornerRadius(8)
    }
}

【问题讨论】:

  • 使用LazyVStack 怎么样,这还会导致性能问题吗?
  • 是的,因为出于性能原因我将其尺寸缩小了,但 LazyVStack 会强制它填满所有可用空间
  • 我应该提到的另一件事是卡片是堆叠在一起的,所以我认为惰性堆栈仍然会渲染它们,因为它们在视图中都是可见的,只是彼此重叠

标签: swift xcode swiftui


【解决方案1】:

VStack 要渲染里面的每个视图,你需要使用其中一个惰性视图:

  1. 列表,它是UITableView 的包装器。这个的优点是能够使用许多表格视图功能,例如通过滑动删除单元格、重新排序等,但如果您有时不这样做,则很难自定义,例如当您想删除分隔符时等。在apple lists tutorial 中查看更多信息。我建议你通过所有的苹果教程,因为它们非常好
List {
    ForEach(imageObjectGroup){ card in
        CardView(card: card).padding(8)
    }.zIndex(1.0)
}
  1. 第二种方式是LazyVStack 包裹在ScrollView 中。它就像VStack 一样工作,但以惰性方式加载单元格。在this article 中查看更多信息
ScrollView {
    LazyVStack {
        ForEach(imageObjectGroup){ card in
            CardView(card: card).padding(8)
        }.zIndex(1.0)
    }
}

附言我不知道你为什么用ZStack包装你的卡,这里是多余的

【讨论】:

  • 我试过 LazyVStack 但它有一个独特的尺寸属性,可以改变卡片图像的大小。出于性能原因,我目前将它们的尺寸缩小了,但 LazyVStack 会强制该项目占据所有宽度,从而使应用程序滞后。
  • @Sachu 如果您想获得有关如何修复滞后的建议,您需要更新您的示例,以便仅通过复制粘贴即可轻松运行。第一个建议是getAssetThumbnail 可能需要很长时间,因为每次视图重新渲染时,您都会以同步方式从存储中获取图像,您应该将此逻辑移动到视图模型或帮助器类
  • 这很好,我会尝试将 PHAsset 转换为 UIImage 并在渲染它们之前存储它们以提高性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多