SwiftUI 2 matchedGeometryEffect: order of modifiers

Viewed 194

I have been breaking my brains on creating a desired matchedGeometryEffect with SwiftUI 2. During this learning process, something passed my path that I really can't explain. I'll show an example.

I try to create a List -> Detail transition, with an effect commonly called "Hero" effect. The profile picture of the list view transitions to the big banner on the detail view.

To accomplish this, I have created two KFImages (KF = Kingfisher, a package I use for conveniently downloading and caching images) with several modifiers:

List View:

if !isProductDetailViewPresented {
   KFImage(store.results.first?.images?.first?.url)
      .resizable()
      .cornerRadius(30)
      .scaledToFill()
      .matchedGeometryEffect(id: "image.testprod", in: animationNamespace!, anchor: .center, isSource: !isProductDetailViewPresented)
      .animation(.easeInOut(duration: 0.3))
      .frame(width: 60, height: 60, alignment: .center)
      .zIndex(999)
}

Detail View:

if isProductDetailViewPresented {

...

    KFImage(product?.images?.first?.url)
        .resizable()
        .scaledToFill()
        .cornerRadius(30)
        .matchedGeometryEffect(id: "image.testprod", in: animationNamespace!, anchor: .center, isSource: isProductDetailViewPresented)
        .frame(height: 300)
        .animation(.easeInOut(duration: 0.3))

...

}

...this nearly worked; somehow, my Image won't constrain to the .frame(width: 60, height: 60). Instead, it stretches to fit the image into the view, making the width bigger than 60. The height stays at 60 though.

To fix this, I figured; maybe this .frame() modifier should be placed before the .matchedGeometryEffect() modifier. But when I do that, only the position of the two images transitions; not the size. This makes me think that the .matchedGeometryEffect() modifiers consider the frames 'fixed' to the placement of the .frame() modifiers before the .matchedGeometryEffect() modifiers.

This is strange, as several examples on the internet show code in which .frame() is called before .matchedGeometryEffect(), like here (search for .matchedGeometryEffect(), there are only 2) and here (section "Follow the Follower").

Two questions:

  1. How come that mine works only when I call .frame() after .matchedGeometryEffect(), while the online sources do the inverse?

  2. How does .matchedGeometryEffect() handle the order of modifiers? What should I take into account?

Honestly, I am having quite a bad time figuring this API out. I hope you guys can clear things up for me. :)

0 Answers
Related