Is it possible to have three (more than 2!) views that are animated in and out from the sides according to an Picker
with the SegmentedPickerStyle()
style?
Having only 2 items in the picker makes the job static and thus a .transition(.move(edge: .leading))
on the first and a .transition(.move(edge: .trailing))
on the last view is sufficient. But what about more than 2?
Maybe there is a way to make the edge: ...
parameter of the transition dynamically set to .leading
or .trailing
and depending on whether how the new value of the binding is greater or less than the current on.
I created the following sketch to demonstrate the issue of having the same view (view 2) with a different transition depending on whether a view on its right or left is selected. Please see this only as an example, the problem is not restricted to only three segments, nor only the middle view, nor only disappearing transitions etc..
Update The secret sauce is to make sure the transition fires with an animation and tell it what direction to go before doing so. Give this a try:
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())
}
}