4

The following SwiftUI view demonstrates an animation glitch that seems to be caused by ViewThatFits. To see the issue, render the following view and tap on the red section. To see that it works without the ViewThatFits tap on the blue section. To be specific, the glitch that I am observing is that the purple view snaps instantly into its new position on the screen without respecting the animated transition, while the green view performs the transition as expected. This glitch does not occur if the purple view is not wrapped inside of ViewThatFits.

///
struct ViewThatFitsAnimationGlitchDemo: View {
    
    ///
    @State var displayedSubpage: Subpage? = nil
    
    ///
    enum Subpage {
        case normal
        case glitchy
    }
    
    ///
    var body: some View {
        
        ///
        VStack(spacing: 0) {
            tappableColor(.blue) {
                withAnimation {
                    displayedSubpage = .normal
                }
            }
            tappableColor(.red) {
                withAnimation {
                    displayedSubpage = .glitchy
                }
            }
        }
            .overlay {
                if let displayedSubpage {
                    switch displayedSubpage {
                    case .normal:
                        normalSubpage
                            .transition(.move(edge: .trailing))
                    case .glitchy:
                        glitchySubpage
                            .transition(.move(edge: .trailing))
                    }
                }
            }
    }
    
    ///
    var normalSubpage: some View {
        VStack(spacing: 0) {
            
            ///
            Color.purple
            
            ///
            tappableColor(.green) {
                withAnimation {
                    displayedSubpage = nil
                }
            }
        }
    }
    
    ///
    var glitchySubpage: some View {
        VStack(spacing: 0) {
            
            ///
            ViewThatFits {
                Color.purple
            }
            
            ///
            tappableColor(.green) {
                withAnimation {
                    displayedSubpage = nil
                }
            }
        }
    }
    
    ///
    func tappableColor
        (_ color: Color,
         onPressed: @escaping ()->())
    -> some View {
        
        ///
        Button(
            action: onPressed,
            label: { color }
        )
            .buttonStyle(.plain)
    }
}

1 Answer 1

1

iOS 17 and above

It works when you add .geometryGroup() to ViewThatFits:

ViewThatFits {
    Color.purple
}
. geometryGroup()

Earlier iOS versions

The modifier .drawingGroup() also works. However, this modifier is more likely to cause other side effects, so you probably want to use with caution.

5
  • 1
    That's true, but .drawingGroup() seems to introduce some rendering differences (I'm seeing a shadow being cutoff). I'm sure with some padding I could jerry-rig it to not get cutoff, but it's not an ideal solution. Commented Aug 20, 2023 at 19:17
  • This was working well for me until I realised that if display zoom is turned ON, and if the inner view is a scroll view, (i.e ScrollView { Color.purple } in the example above) the drawing group doesn't render whatsoever. FB13798255
    – Tricky
    Commented May 14 at 20:29
  • @Tricky Sounds like a candidate for a new question with an MRE to illustrate the new problem.
    – Benzy Neez
    Commented May 14 at 20:39
  • @BenzyNeez it's a bug in SwiftUI, unfortunately. Hopefully iOS 18 will bring some relief.
    – Tricky
    Commented May 14 at 21:11
  • Answer revised to use geometryGroup() in preference to drawingGroup(), if available.
    – Benzy Neez
    Commented Oct 10 at 8:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.