我有这个:
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://stackoverflow.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 ------
嗯,这个假设是错误的。我现在对问题的结构理解得更清楚了。
点击其中一个AlbumItemsScreenCell
将导航到一个详细信息屏幕(我已经添加到上面的代码中以显示这一点)。在详细信息屏幕中,用户操作可以导致评论计数重置,从而发送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://stackoverflow.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)
}
}
当Notification
被接收时,unreadCountBadgeText
被观察者改变(在主线程上)。
因此,总的来说,单元格上的徽章被更改,而不显示单元格的屏幕—显示详细信息屏幕。
我一直在使用以下条件修饰语:
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
)
}
}
这可能听起来很奇怪,但在我的tvOS应用程序中,我禁用了动画和单元格(视图)停止消失所以试着删除.animation(.easeInOut)