I'm trying to animate the two squares square
below from a common center point to their final position when they appear, and back - when they are removed.
Here's what I tried, but the result it that they don't start from an overlapping position - rather, they start close to to each in the center:
struct ContentView: View {
@State var matched = true
@State var show = false
@Namespace var ns
var body: some View {
VStack {
HStack {
Spacer()
if show {
square
.matchedGeometryEffect(id: matched ? "match" : "",
in: ns, anchor: .center, isSource: false)
.animation(.easeIn)
.transition(.move(edge: .trailing ))
.onAppear { withAnimation { matched = false } }
.onDisappear { withAnimation { matched = true } }
}
Spacer()
.matchedGeometryEffect(id: "match", in: ns, anchor: .center, isSource: true)
if show {
square
.matchedGeometryEffect(id: matched ? "match" : ""
in: ns, anchor: .center, isSource: false)
.animation(.easeIn)
.transition(.move(edge: .leading))
}
Spacer()
}
Button("show") { withAnimation { show.toggle() } }
}
}
}
The square square
is simply defined as:
var square: some View {
Rectangle().foregroundColor(.blue)
.frame(width: 40, height: 40, alignment: .center)
}
What sort of worked was to attach matchedGeometryEffect
to an overlay within Spacer
, and also to explicitly specify properties: .position
in all of the them.
Still, it only works when they appear, but not when disappearing; there's still a gap there.
Spacer().overlay(
Color.clear
.matchedGeometryEffect(id: "match", in: ns, properties: .position, anchor: .center, isSource: true)
)
Is this the right general approach to achieve this effect, and if so, how can I make it work? Or have I overcomplicated it?
Try with geometry matching another clear square with the same dimension and simplified a bit. There's an intrinsic opacity transition that you might want to remove and replace with some color blend mode.
struct ContentView: View {
//@State var matched = true
@State private var show = false
@Namespace private var ns
var body: some View {
VStack {
HStack {
Spacer()
if show {
Square(color: .blue)
.matchedGeometryEffect(id: 1, in: ns, isSource: false)
}
Spacer()
.background(Square(color: .clear)
.matchedGeometryEffect(id: show ? 0 : 1, in: ns, isSource: true))
if show {
Square(color: .red)
.matchedGeometryEffect(id: 1, in: ns, isSource: false)
}
Spacer()
}
Button("show") { withAnimation { show.toggle() } }
}
}
struct Square: View {
let color: Color
var width: CGFloat = 40
var body: some View {
Rectangle().foregroundColor(color).frame(width: width, height: width)
}
}
}