我正在创建一个水平菜单,并使用水平ScrollViewHStack来查看其项目.一切似乎都很好用,但我对ScrollViewReader有点问题.正如您应该从代码中看到的那样,当currentIndex更改时,我将其值传递给proxy.scrollTo()方法,但我没有得到任何结果,ScrollView没有遵循currentIndex的修改,而currentIndex仍然保持在其原始位置.你能告诉我我哪里错了吗?谢谢你们所有人

struct ScrollableTabMenu<T:CaseIterable & Hashable & Identifiable>: View  {
    var items: [T]
    var title: KeyPath<T, String>
    @Binding var currentIndex: Int
    @Namespace private var animation
    
    var body: some View {
                  
        ScrollView(.horizontal) {
            ScrollViewReader { proxy in
                HStack {
                    ForEach(items) { screen in
                        let index = items.firstIndex(of: screen)!
                        Button {
                            currentIndex = index
                        } label: {
                            Text(screen[keyPath: title])
                                .padding()
                                .overlay(alignment: .bottom){
                                    if currentIndex == index  {
                                        RoundedRectangle(cornerRadius: 6)
                                            .frame(height: 1)
                                            .matchedGeometryEffect(id: "AninamtionTAB", in: animation)
                                    }
                                }
                        }
                        .buttonStyle(.plain)
                    }
                }
                .onChange(of: currentIndex, { _, newValue in
                    withAnimation {
                        proxy.scrollTo(newValue, anchor: .leading)
                        print(newValue)
                    }
                })
            }
        }
        .scrollTargetBehavior(.paging)
        .scrollIndicators(.hidden)
        .contentMargins(.horizontal, 16)
    }
}

推荐答案

scrollTo取您要滚动到的视图的ID.您的所有按钮都具有ForEach自动给出的ID,它是itemsT的对应值,而不是Int索引号.

如果你想使用索引作为id,你应该在按钮上加.id(index).

然而,索引非常不稳定--每当您添加/删除选项卡时,索引都会发生变化.我建议用Binding<T>来代表当前的标签.

@Binding var currentTab: T
var body: some View {
              
    ScrollView(.horizontal) {
        ScrollViewReader { proxy in
            HStack {
                ForEach(items) { screen in
                    let index = items.firstIndex(of: screen)!
                    Button {
                        currentTab = screen
                    } label: {
                        Text(screen[keyPath: title])
                            .padding()
                            .overlay(alignment: .bottom){
                                if currentTab == screen  {
                                    RoundedRectangle(cornerRadius: 6)
                                        .frame(height: 1)
                                        .matchedGeometryEffect(id: "AninamtionTAB", in: animation)
                                }
                            }
                    }
                    .buttonStyle(.plain)
                }
            }
            .onChange(of: currentTab, { _, newValue in
                withAnimation {
                    proxy.scrollTo(newValue, anchor: .leading)
                }
            })

请注意,现在您甚至不需要添加您自己的.id(screen)-ForEach自动分配给按钮的ID正是我们希望它们拥有的ID.

在这种情况下,使用.paging滚动行为是相当奇怪的.页面不一定与选项卡按钮对齐,所以当你点击一个选项卡时,按钮会向左滚动,并可能被"切断",因为这恰好是一个新的"页面"开始的地方.

我更喜欢使用.scrollTargetBehavior(.viewAligned),并将HStack设置为.scrollTargetLayout().

我也不会使用KeyPath<T, String>.这不允许本地化.我会接受(T) -> Text(T) -> Label,其中Label是约束为View的泛型类型参数.

Ios相关问答推荐

如何调整包含extView的UICollectionViewListCell的大小?

自定义油漆圆角三角形

TrueDepth摄像机像素距离不准确

UIAlertController的空子类是否可以与`appearance(whenContainedInInstancesOf:)`一起安全使用

SwiftUI:.toolbar 不适用于 UITextView

swiftui 图像放大时无法正确拖动

React Native IOS base64 编码图像不显示

为什么常量属性可能在 Swift 类中被初始化两次?

在 SwiftUI 中重构提取到子视图的 GeometryReader 代码

从iOS键盘输入时Flutter TextField输入消失

使用 Apollo 发布 iOS 应用程序时缺少推送通知权利

JSONDecoder 在模拟器上运行良好时无法在真实设备上解码

从远程通知启动时,iOS 应用程序加载错误

Xcode 项目格式:3.1、3.2、6.3 和 8.0 有什么区别?

在 Grand Central Dispatch 中使用 dispatch_sync

interfaceOrientation在 iOS 8 中已弃用,如何更改此方法 Objective C

iPhone 未连接.连接 iPhone 后 Xcode 将继续

iOS中的apk类似功能是什么?

CoreBluetooth 应用程序到底能在后台做什么?

如何检测快速touch 或单击的tableView单元格