SwiftUI:使用matchedGeometryEffect控制视图上的zIndex
Posted
技术标签:
【中文标题】SwiftUI:使用matchedGeometryEffect控制视图上的zIndex【英文标题】:SwiftUI: control zIndex on views with matchedGeometryEffect 【发布时间】:2021-03-10 11:02:51 【问题描述】:我正在 SwiftUI 中构建一个自定义 SegmentedPicker,其中选择器会调整其大小以适应每个选择器项目的框架。我已经在这篇文章 (Inspecting the View Tree) 的启发下使用 PreferenceKey
s 来处理统一大小的项目,如下所示:
我认为我可以通过使用.matchedGeometryEffect()
来大大简化我的实现并完全避免使用PreferencyKey
s。我的想法是仅在选择该项目时在每个项目后面显示一个选择器,并使用 .matchedGeometryEffect()
同步过渡。除了选择器将位于先前选择的项目前面的问题之外,几乎一切都在工作。我尝试显式设置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
:
struct ContentView: View
@State private var selection = 0
let pickerItems = [ "Item 1", "Long item 2", "Item 3", "Item 4", "Long item 5"]
var body: some View
MatchedGeometryPicker(selection: $selection, items: pickerItems)
.background(Color.gray.opacity(0.10).clipShape(Capsule()))
.padding(.horizontal, 5)
任何想法如何解决这个问题?
【问题讨论】:
为什么使用 PreferenceKeys 的工作代码不适合使用和尝试这种方式? @swiftPunk with PreferenceKeys 如果每个项目都具有相同的框架,它会很好地工作,但我仍在尝试解决选择器尺寸的动画问题。使用.matchedGeometryEffect()
,选择器可以完美地动画位置和尺寸变化,并且代码更简单。我只需要弄清楚如何解决分层问题...
【参考方案1】:
当项目具有不同的帧大小时,我设法解决了使用 PreferenceKey
s 的选择器实现时遇到的所有动画问题。这并不能解决我在使用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)
结果:
【讨论】:
以上是关于SwiftUI:使用matchedGeometryEffect控制视图上的zIndex的主要内容,如果未能解决你的问题,请参考以下文章
在SwiftUI项目中使用UIKit(SwiftUI和UIKit混合开发)
SwiftUI 使用SwiftUI实现跑马灯效果 Marquee