SwiftUI:使用matchedGeometryEffect 控制视图上的zIndex

问题描述

我正在 SwiftUI 中构建自定义 SegmentedPicker,其中选择器调整其大小以适合每个选择器项目的框架。我已经按照这篇文章 (Inspecting the View Tree) 的启发使用了 { "type": "Microsoft.Compute/virtualMachines/extensions","name": "[concat(parameters('vmName'),'/installcustomscript')]","apiVersion": "2019-03-01","location": "[parameters('location')]","properties": { "publisher": "Microsoft.Azure.Extensions","type": "CustomScript","typeHandlerVersion": "2.1","autoUpgradeMinorVersion": true,"settings": { "fileUris": ["https://mystorageaccount.blob.core.windows.net/oms/oms_linux.sh"] },"protectedSettings": { "commandToExecute": "wget https://raw.githubusercontent.com/Microsoft/OMS-Agent-for-Linux/master/installer/scripts/onboard_agent.sh && sh oms_linux.sh","storageAccountName": "xxx","storageAccountKey": "xxxx" } } } 来制作尺寸统一的项目,如下所示:

enter image description here

我认为我可以通过使用 PreferenceKey 来大大简化我的实现并避免完全使用 PreferencyKey。我的想法是仅在每个项目被选中时才在每个项目后面显示一个选择器,并使用 .matchedGeometryEffect() 同步转换。除了选择器位于先前选择的项目前面的问题外,几乎所有东西都在工作。我尝试明确设置 .matchedGeometryEffect(),但它似乎不会影响结果:

enter image description here

代码

zIndex

struct MatchedGeometryPicker: View { @Namespace private var animation @Binding var selection: Int let items: [String] var body: some View { HStack { ForEach(items.indices) { index in ZStack { if isSelected(index) { Color.gray.clipShape(Capsule()) .matchedGeometryEffect(id: "selector",in: animation) .animation(.easeInOut) .zIndex(0) } itemView(for: index) .padding(7) .zIndex(1) } .fixedSize() } } .padding(7) } func itemView(for index: Int) -> some View { Text(items[index]) .frame(minWidth: 0,maxWidth: .infinity) .foregroundColor(isSelected(index) ? .black : .gray) .font(.caption) .onTapGesture { selection = index } } func isSelected(_ index: Int) -> Bool { selection == index } } 中:

ContentView

任何想法如何解决这个问题?

解决方法

当项目具有不同的帧大小时,我设法解决了我在使用 PreferenceKey 的选择器实现中遇到的所有动画问题。这不能解决我对 zIndex.matchedGeometryEffect() 的问题,因此我不会接受我自己的答案,但我会将其发布为参考,以防将来有人需要它。

代码:

public struct PKPicker: View {
    @Binding var selection: Int
    @State private var frames: [CGRect] = []
    let items: [String]
    
    public init(
        selection: Binding<Int>,items: [String])
    {
        self._selection = selection
        self._frames = State(wrappedValue: Array<CGRect>(repeating: CGRect(),count: items.count))
        self.items = items
    }
    
    public var body: some View {
        ZStack(alignment: .topLeading) {
            selector
            HStack {
                ForEach(items.indices)  { index in
                    itemView(for: index)
                        
                }
            }
        }
        .onPreferenceChange(PKPickerItemPreferenceKey.self) { preferences in
            preferences.forEach { frames[$0.id] = $0.frame }
        }
        .coordinateSpace(name: "picker2")
        
    }
    
    var selector: some View {
        Color.gray.opacity(0.25).clipShape(Capsule())
            .frame(width: frames[selection].size.width,height: frames[selection].size.height)
            .offset(x: frames[selection].minX,y: frames[selection].minY)
    }
    
    func itemView(for index: Int) -> some View {
        Text(items[index])
            .fixedSize()
            .padding(7)
            .foregroundColor(isSelected(index) ? .black : .gray)
            .font( .caption)
            .onTapGesture { selection = index }
            .background(PKPickerItemPreferenceSetter(id: index))
            
    }
    
    func isSelected(_ index: Int) -> Bool {
        index == selection
    }
}

struct PKPickerItemPreferenceData: Equatable {
    let id: Int
    let frame: CGRect
}

struct PKPickerItemPreferenceKey: PreferenceKey {
    typealias Value = [PKPickerItemPreferenceData]

    static var defaultValue: [PKPickerItemPreferenceData] = []
    
    static func reduce(
        value: inout [PKPickerItemPreferenceData],nextValue: () -> [PKPickerItemPreferenceData])
    {
        value.append(contentsOf: nextValue())
    }
}

struct PKPickerItemPreferenceSetter: View {
    let id: Int
    let coordinateSpace = CoordinateSpace.named("picker2")
    
    var body: some View {
        GeometryReader { geometry in
            Color.clear
                .preference(key: PKPickerItemPreferenceKey.self,value: [PKPickerItemPreferenceData(
                                        id: id,frame: geometry.frame(in: coordinateSpace))])
        }
    }
}

ContentView

结构内容视图:查看{ @State 私有变量选择 = 0

let pickerItems = [ "Item 1","Long item 2","Item 3","Item 4","Long Item 5"]

var body: some View {
    PKPicker(selection: $selection.animation(.easeInOut),items: pickerItems)
        .padding(7)
        .background(Color.gray.opacity(0.10).clipShape(Capsule()))
        .padding(5)
    
}

结果:

enter image description here

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...