SwiftUI has the default slide transition .transition(.slide)
. It works well, but only halfway. I mean, I have to slide my views and then slide them back (I need the animation backwards). Since it is not possible to set the direction of the slide transition, I implemented a custom transition. The implementation is simple enough and works well. But I can't get the view size to calculate the start and end offset points.
extension AnyTransition {
static func slide(direction: SlideModifier.Direction) -> AnyTransition {
.asymmetric(insertion: .modifier(active: SlideModifier(positiveOffset: true, direction: direction), identity: SlideModifier(positiveOffset: nil, direction: direction)),
removal: .modifier(active: SlideModifier(positiveOffset: false, direction: direction), identity: SlideModifier(positiveOffset: nil, direction: direction)))
}
}
internal struct SlideModifier: ViewModifier {
let positiveOffset: Bool?
let direction: Direction
let offsetSize: CGFloat = 200 // How can I get this value programmatically?
enum Direction {
case leading, top, trailing, bottom
}
func body(content: Content) -> some View {
switch direction {
case .leading:
return content.offset(x: positiveOffset == nil ? 0 : (positiveOffset! ? offsetSize : -offsetSize))
case .top:
return content.offset(y: positiveOffset == nil ? 0 : (positiveOffset! ? offsetSize : -offsetSize))
case .trailing:
return content.offset(x: positiveOffset == nil ? 0 : (positiveOffset! ? -offsetSize : offsetSize))
case .bottom:
return content.offset(y: positiveOffset == nil ? 0 : (positiveOffset! ? -offsetSize : offsetSize))
}
}
}
I want to define the offset size that is equal to the view size. My experiments have shown that this is how the standard slide transition works. I tried using GeometryReader
and PreferenceKey
to get the size. Maybe I did something wrong, but it didn't work.
Here's a simple example of using my custom transition.
struct ContentView: View {
@State private var isShowingRed = false
var body: some View {
ZStack {
if isShowingRed {
Color.red
.frame(width: 200, height: 200)
.transition(.slide(direction: .trailing))
} else {
Color.blue
.frame(width: 200, height: 200)
.transition(.slide(direction: .leading))
}
}
.onTapGesture {
withAnimation {
isShowingRed.toggle()
}
}
}
}