LazyVGrid 列表中的单元格有时会消失

Posted

技术标签:

【中文标题】LazyVGrid 列表中的单元格有时会消失【英文标题】:Cell in List with LazyVGrid Disappears sometimes 【发布时间】:2021-10-31 01:08:40 【问题描述】:

我有这个:

        VStack             
            List 
                LazyVGrid(columns: gridItemLayout) 
                    ForEach(viewModel.objects, id: \.fileGroupUUID)  item in
                        AlbumItemsScreenCell(object: item, viewModel: viewModel, config: Self.config)
                            .onTapGesture 
                                switch viewModel.changeMode 
                                case .moving, .sharing, .moveAll:
                                    viewModel.toggleItemToChange(item: item)
                                case .none:
                                    object = item
                                    viewModel.showCellDetails = true
                                
                            
                            .onLongPressGesture 
                                viewModel.restartDownload(fileGroupUUID: item.fileGroupUUID)
                            
                     // end ForEach
                 // end LazyVGrid
            
            .listStyle(PlainListStyle())
            .refreshable 
                viewModel.refresh()
            
            .padding(5)
            // Mostly this is to animate updates from the menu. E.g., the sorting order.
            .animation(.easeInOut)
            
            // Had a problem with return animation for a while: https://***.com/questions/65101561
            // The solution was to take the NavigationLink out of the scrollview/LazyVGrid above.
            if let object = object 
                // The `NavigationLink` works here because the `MenuNavBar` contains a `NavigationView`.
                NavigationLink(
                    destination:
                        ObjectDetailsView(object: object, model: ObjectDetailsModel(object: object)),
                    isActive:
                        $viewModel.showCellDetails) 
                    EmptyView()
                
                .frame(width: 0, height: 0)
                .disabled(true)
             // end if
         // end VStack

AlbumItemsScreenCell:

struct AlbumItemsScreenCell: View 
    @StateObject var object:ServerObjectModel
    @StateObject var viewModel:AlbumItemsViewModel
    let config: IconConfig
    @Environment(\.colorScheme) var colorScheme

    var body: some View 
        AnyIcon(model: AnyIconModel(object: object), config: config,
            emptyUpperRightView: viewModel.changeMode == .none,
            upperRightView: 
                UpperRightChangeIcon(object: object, viewModel: viewModel)
            )
    

当用户点击其中一个单元格时,会导致导航到详细信息屏幕。有时当用户从该导航返回时,左上角的单元格会消失:

https://www.dropbox.com/s/mi6j2ie7h8dcdm0/disappearingCell.mp4?dl=0

我目前关于该问题的假设是,当该详细信息屏幕中的用户操作采取更改 viewModel.objects 的操作时,这会导致单元格消失问题。我很快就会检验这个假设。

----- 更新,11/1/21 ------

嗯,这个假设是错误的。我现在更清楚地了解了问题的结构。但仍然没有修复。

点击其中一个AlbumItemsScreenCells 导航到详细信息屏幕(我已添加到上面的代码中以显示这一点)。在详细信息屏幕中,用户操作可能会导致评论计数被重置,这会发送Notification

AlbumItemsScreenCell 中的模型侦听这些通知(针对特定单元格)并重置单元格上的徽章。

这是那个模型:

class AnyIconModel: ObservableObject, CommentCountsObserverDelegate, MediaItemBadgeObserverDelegate, NewItemBadgeObserverDelegate 
    @Published var mediaItemBadge: MediaItemBadge?
    @Published var unreadCountBadgeText: String?
    @Published var newItem: Bool = false
    var mediaItemCommentCount:CommentCountsObserver!
    let object: ServerObjectModel
    var mediaItemBadgeObserver: MediaItemBadgeObserver!
    var newItemObserver: NewItemBadgeObserver!
    
    init(object: ServerObjectModel) 
        self.object = object
        
        // This is causing https://***.com/questions/69783232/cell-in-list-with-lazyvgrid-disappears-sometimes
        mediaItemCommentCount = CommentCountsObserver(object: object, delegate: self)
        
        mediaItemBadgeObserver = MediaItemBadgeObserver(object: object, delegate: self)
        newItemObserver = NewItemBadgeObserver(object: object, delegate: self)
    

unreadCountBadgeText 在收到Notification 时被观察者更改(在主线程上)。

因此,总而言之,单元格上的徽章会发生变化,而单元格的屏幕不会显示 - 会显示详细信息屏幕。

【问题讨论】:

你能加入minimal reproducible example吗?否则,任何人都无法运行您的代码并尝试帮助调试它。 看起来AlbumItemsScreenCell 中的 2 @StateObject 应该是 @ObservedObject。一旦您向我们展示了一个可重现的最小示例,我们就会了解更多。 【参考方案1】:

我一直在使用以下条件修饰符:

extension View 
    public func enabled(_ enabled: Bool) -> some View 
        return self.disabled(!enabled)
    
    
    // https://forums.swift.org/t/conditionally-apply-modifier-in-swiftui/32815/16
    @ViewBuilder func `if`<T>(_ condition: Bool, transform: (Self) -> T) -> some View where T : View 
        if condition 
            transform(self)
         else 
            self
        
    

AlbumItemsScreenCell 上显示徽章。

原来的徽章是这样的:

extension View 
    func upperLeftBadge(_ badgeText: String) -> some View 
        return self.modifier(UpperLeftBadge(badgeText))
    


struct UpperLeftBadge: ViewModifier 
    let badgeText: String
    init(_ badgeText: String) 
        self.badgeText = badgeText
    

    func body(content: Content) -> some View 
        content
            .overlay(
                ZStack 
                    Badge(badgeText)
                
                .padding([.top, .leading], 5),
                alignment: .topLeading
            )
    

即,单元格中的用法如下所示:

        .if(condition) 
            $0.upperLeftBadge(badgeText)
        

当我更改修饰符以使用它而不使用这个 .if 修饰符并直接使用它时,问题就消失了:

extension View 
    func upperLeftBadge(_ badgeText: String?) -> some View 
        return self.modifier(UpperLeftBadge(badgeText: badgeText))
    


struct UpperLeftBadge: ViewModifier 
    let badgeText: String?

    func body(content: Content) -> some View 
        content
            .overlay(
                ZStack 
                    if let badgeText = badgeText 
                        Badge(badgeText)
                    
                
                .padding([.top, .leading], 5),
                alignment: .topLeading
            )
    

【讨论】:

以上是关于LazyVGrid 列表中的单元格有时会消失的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView 单元格消失

单击单元格本身而不是按钮时,(静态)单元格中的按钮会消失

如何正确地将“单元格项目”从 SwiftUI LazyVGrid 传递给 .sheet?

如何正确地将“单元格项目”从 SwiftUI LazyVGrid 传递给 .sheet?

只有在 iOS 7 中点击单元格时,tableview 单元格中的 labeltext 才会消失

在表格视图单元格之间移动的文本