与分段选择器匹配的 SwiftUI 视图转换
SwiftUI View Transition matching to an Segmented Picker
是否可以根据 Picker
和 SegmentedPickerStyle()
样式让三个(超过 2 个!)动画视图从侧面进出动画?
选择器中只有 2 个项目会使作业静态化,因此第一个视图上的 .transition(.move(edge: .leading))
和最后一个视图上的 .transition(.move(edge: .trailing))
就足够了。但是超过 2 个呢?
也许有一种方法可以使转换的 edge: ...
参数动态设置为 .leading
或 .trailing
,这取决于绑定的新值是更大还是更大小于当前开启。
我创建了以下草图来演示具有不同过渡的相同视图(视图 2)的问题,具体取决于选择的是右侧还是左侧的视图。请仅作为示例查看,问题不仅限于三段,也不仅限于中间视图,也不仅限于消失的过渡等。
Update 秘诀是确保通过动画触发过渡,并在这样做之前告诉它前进的方向。试一试:
class TabControllerContext : ObservableObject {
@Published var selected = panels.one { didSet {
if previous != selected {
insertion = selected.makeMove(previous)
removal = previous.makeMove(selected)
withAnimation {
trigger = selected
previous = selected
}
}
}}
@Published var trigger = panels.one
@Published var previous = panels.one
var insertion : AnyTransition = .move(edge: .leading)
var removal : AnyTransition = .move(edge: .trailing)
}
struct TabsWithTransitionsView: View {
@EnvironmentObject var context : TabControllerContext
var body: some View {
VStack {
Picker("Select Panel", selection: $context.selected) {
ForEach(panels.allCases) { panel in
panel.label.tag(panel)
}
}.pickerStyle(SegmentedPickerStyle())
ForEach(panels.allCases) { panel in
if context.trigger == panel {
panel.label
.background(panel.color)
.transition(.asymmetric(insertion: context.insertion, removal: context.removal))
}
}
}
}
}
enum panels : Int, CaseIterable, Identifiable {
case one = 1
case two = 2
case three = 3
var label : some View {
switch self {
case .one:
return Label("Tab One", systemImage: "1.circle")
case .two:
return Label("Tab Two", systemImage: "2.square")
case .three:
return Label("Tab Three", systemImage: "asterisk.circle")
}
}
var color : Color {
switch self {
case .one: return Color.red.opacity(0.5)
case .two: return Color.green.opacity(0.5)
case .three: return Color.blue.opacity(0.5)
}
}
func makeMove(_ otherPanel: panels) -> AnyTransition {
return otherPanel.rawValue < self.rawValue ? .move(edge: .trailing) : .move(edge: .leading)
}
// so the enum can be indentified when enumerated
var id : Int { self.rawValue }
}
struct TabsWithTransitionsView_Previews: PreviewProvider {
static var previews: some View {
TabsWithTransitionsView().environmentObject(TabControllerContext())
}
}
是否可以根据 Picker
和 SegmentedPickerStyle()
样式让三个(超过 2 个!)动画视图从侧面进出动画?
选择器中只有 2 个项目会使作业静态化,因此第一个视图上的 .transition(.move(edge: .leading))
和最后一个视图上的 .transition(.move(edge: .trailing))
就足够了。但是超过 2 个呢?
也许有一种方法可以使转换的 edge: ...
参数动态设置为 .leading
或 .trailing
,这取决于绑定的新值是更大还是更大小于当前开启。
我创建了以下草图来演示具有不同过渡的相同视图(视图 2)的问题,具体取决于选择的是右侧还是左侧的视图。请仅作为示例查看,问题不仅限于三段,也不仅限于中间视图,也不仅限于消失的过渡等。
Update 秘诀是确保通过动画触发过渡,并在这样做之前告诉它前进的方向。试一试:
class TabControllerContext : ObservableObject {
@Published var selected = panels.one { didSet {
if previous != selected {
insertion = selected.makeMove(previous)
removal = previous.makeMove(selected)
withAnimation {
trigger = selected
previous = selected
}
}
}}
@Published var trigger = panels.one
@Published var previous = panels.one
var insertion : AnyTransition = .move(edge: .leading)
var removal : AnyTransition = .move(edge: .trailing)
}
struct TabsWithTransitionsView: View {
@EnvironmentObject var context : TabControllerContext
var body: some View {
VStack {
Picker("Select Panel", selection: $context.selected) {
ForEach(panels.allCases) { panel in
panel.label.tag(panel)
}
}.pickerStyle(SegmentedPickerStyle())
ForEach(panels.allCases) { panel in
if context.trigger == panel {
panel.label
.background(panel.color)
.transition(.asymmetric(insertion: context.insertion, removal: context.removal))
}
}
}
}
}
enum panels : Int, CaseIterable, Identifiable {
case one = 1
case two = 2
case three = 3
var label : some View {
switch self {
case .one:
return Label("Tab One", systemImage: "1.circle")
case .two:
return Label("Tab Two", systemImage: "2.square")
case .three:
return Label("Tab Three", systemImage: "asterisk.circle")
}
}
var color : Color {
switch self {
case .one: return Color.red.opacity(0.5)
case .two: return Color.green.opacity(0.5)
case .three: return Color.blue.opacity(0.5)
}
}
func makeMove(_ otherPanel: panels) -> AnyTransition {
return otherPanel.rawValue < self.rawValue ? .move(edge: .trailing) : .move(edge: .leading)
}
// so the enum can be indentified when enumerated
var id : Int { self.rawValue }
}
struct TabsWithTransitionsView_Previews: PreviewProvider {
static var previews: some View {
TabsWithTransitionsView().environmentObject(TabControllerContext())
}
}