SwiftUI List 在任何视图更改时重置滚动

Posted

技术标签:

【中文标题】SwiftUI List 在任何视图更改时重置滚动【英文标题】:SwiftUI List reset scroll on any view change 【发布时间】:2021-04-20 07:47:04 【问题描述】:

我有一个非常简单的列表,有一些部分,在同一视图中,我还有一个按钮在选择了列表项中的任何一个按钮时,它会使用状态变量控制,当发生这种情况时,如果列表是向下滚动,State 变量将更改(启用按钮)并且所有视图将刷新,导致我的列表滚动到顶部。我怎样才能避免这种滚动重置,我应该提到如果元素被删除或添加到列表中也会发生同样的情况,但是,我试图尽可能简化问题,这里是简化的代码 sn-p。

import SwiftUI

enum FavoritesListActiveSheet: Identifiable 
    case moveToSheet
    
    var id: Int  hashValue 


struct Category: Identifiable 
    var id: UUID = UUID()
    var name: String


extension Category 
    static var categories: [Category] 
        [
            Category(name: "category 1"),
            Category(name: "category 2")
        ]
    


struct FavoriteItem: Identifiable 
    var id: UUID = UUID()
    var name: String
    var category: String
    var selected: Bool


extension FavoriteItem 
    static var favoriteItems: [FavoriteItem] 
        [
            FavoriteItem(name: "Item 1", category: "category 1", selected: false),
            FavoriteItem(name: "Item 2", category: "category 1", selected: false),
            FavoriteItem(name: "Item 3", category: "category 1", selected: false),
            FavoriteItem(name: "Item 4", category: "category 2", selected: false),
            FavoriteItem(name: "Item 5", category: "category 2", selected: false),
            FavoriteItem(name: "Item 6", category: "category 2", selected: false),
            FavoriteItem(name: "Item 7", category: "category 2", selected: false),
            FavoriteItem(name: "Item 8", category: "category 2", selected: false),
            FavoriteItem(name: "Item 9", category: "category 2", selected: false),
            FavoriteItem(name: "Item 10", category: "category 2", selected: false),
            FavoriteItem(name: "Item 11", category: "category 2", selected: false),
            FavoriteItem(name: "Item 12", category: "category 2", selected: false),
            FavoriteItem(name: "Item 13", category: "category 2", selected: false),
            FavoriteItem(name: "Item 14", category: "category 2", selected: false),
            FavoriteItem(name: "Item 15", category: "category 2", selected: false),
            FavoriteItem(name: "Item 16", category: "category 2", selected: false),
            FavoriteItem(name: "Item 17", category: "category 2", selected: false),
            FavoriteItem(name: "Item 18", category: "category 2", selected: false),
        ]
    


struct FavoritesRaw: View 

    @Binding var item: FavoriteItem
    @State var refreshView: Bool = false

    let onItemToggle: () -> ()

    var body: some View 
        HStack 
            if (item.selected) 
                Image(systemName: "checkmark.circle")
             else 
                Image(systemName: "circle")
            

            Text (item.name)
        
        .simultaneousGesture(TapGesture().onEnded 

            self.item.selected.toggle()

            refreshView.toggle()
            onItemToggle()
        )
        .contentShape(Rectangle())
    


class FavoritesViewModel: ObservableObject 
    var favorite_items: [FavoriteItem] = FavoriteItem.favoriteItems


struct FavoritesListView: View 
    @StateObject var viewModel: FavoritesViewModel = FavoritesViewModel()

    @State var addtoButtonDisabled: Bool = true
    @State var sheetDisplayed: FavoritesListActiveSheet?

    var body: some View 
        NavigationView 
            VStack 
                List 
                    ForEach (Category.categories)  category in
                        Section (header: Text(category.name))
                        
                            ForEach (self.viewModel.favorite_items.filter($0.category == category.name))  item in
                                FavoritesRaw(item: binding(for: item), onItemToggle: 
                                    addtoButtonDisabled = (self.viewModel.favorite_items.filter($0.selected == true).count == 0)
                                )
                            
                        
                        .textCase(nil)
                        
                    
                
                .listStyle(PlainListStyle())
                .id(UUID()) // no animation
            
            .navigationBarTitle("Favorites", displayMode: .inline)
            .toolbar 
                ToolbarItemGroup(placement: .navigationBarTrailing) 

                    Button(action: 
                        sheetDisplayed = .moveToSheet
                    ) 
                        Text("Add to...")
                    .disabled(addtoButtonDisabled)
                
            
            .sheet(item: $sheetDisplayed)  item in
                // [Show a sheet then disable back the button]
            
        
        .onAppear
        
            addtoButtonDisabled = (self.viewModel.favorite_items.filter($0.selected == true).count == 0)
        
    

    private func binding(for item: FavoriteItem) -> Binding<FavoriteItem> 
        guard let item_index = self.viewModel.favorite_items.firstIndex(where:  $0.id == item.id ) else 
             fatalError("Can't find item in array")
         
        return $viewModel.favorite_items[item_index]
     

【问题讨论】:

【参考方案1】:

好像删除了

.id(UUID()) // 没有动画

正在解决我的问题。但是,我添加了这个来摆脱丑陋的动画,SwiftUI 提供了元素删除。

【讨论】:

以上是关于SwiftUI List 在任何视图更改时重置滚动的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:如何根据滚动视图的用户当前滚动位置同步/更改进度条进度?

SwiftUI - 防止列表在项目选择时将滚动位置重置为顶部

如何在 SwiftUI 中更改滚动视图的滚动方向?

ScrollView 或 List,如何制作特定的可滚动视图 (SwiftUI)

SwiftUI:为 .sheet(_: onDismiss:) 重置状态

为啥在 SwiftUI 中显示视图时 List 的背景颜色不同?