swift – SwiftUI. Subview animation is just not working if subview’s View @State was modified whereas mother or father View is animating. iOS 16


I’ve a construction, the place the mother or father view is a hidden pop-up with content material. On some consumer motion mother or father View begins to slip up. I would like all its content material to slip up with the mother or father View. It was working completely earlier than iOS 16, however now it is damaged. If the kid’s View subview @State is modified throughout the animation, then this View seems immediately on a display screen, i.e. not sliding. As I perceive, as a result of View’s @State was modified SwiftUI redraws this specific View and disables its animation, so it seems in its closing place with out animation. I used to be making an attempt to pressure the animation of this specific View utilizing .animation or withAnimation. Nothing of it helped. Easy methods to repair this bug in iOS 16?

Minimal reproducible instance:

import SwiftUI

@foremost
struct TestApp: App {
    var physique: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
import SwiftUI

struct ContentView: View {

    @State var shouldShow: SlideCardPosition = .backside
    @State var text1: String = ""

    var physique: some View {
        VStack {
            Button {
                shouldShow = .high
            } label: {
                Textual content("Present PopUp")
                    .padding(.high, 100)
            }
            PopUpUIView(shouldShow: $shouldShow)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
import SwiftUI

struct SlideOverCard<Content material: View>: View {
    @GestureState non-public var dragState = SlideDragState.inactive
    @Binding non-public var place: SlideCardPosition
    @State non-public var highlightedBackground = false
    non-public var contentHeight: CGFloat
    non-public var backgroundColor: Colour?
    non-public var withCorners: Bool
    non-public var isHandleHidden: Bool
    non-public var overlayOpacity: CGFloat

    init(place: Binding<SlideCardPosition>,
         contentHeight: CGFloat,
         backgroundColor: Colour? = nil,
         withCorners: Bool = true,
         isHandleHidden: Bool = false,
         overlayOpacity: CGFloat = 0.75,
         content material: @escaping () -> Content material) {
        _position = place
        self.content material = content material
        self.contentHeight = contentHeight
        self.backgroundColor = backgroundColor
        self.withCorners = withCorners
        self.isHandleHidden = isHandleHidden
        self.overlayOpacity = overlayOpacity
    }

    var content material: () -> Content material
    var physique: some View {

        return Rectangle()
            .body(width: UIScreen.screenWidth, peak: UIScreen.screenHeight)
            .foregroundColor(Colour.black.opacity(highlightedBackground ? overlayOpacity : 0))
            .place(x: UIScreen.screenWidth / 2, y: (UIScreen.screenHeight) / 2)
            .edgesIgnoringSafeArea([.top, .bottom])
            .overlay(
                Group {
                    VStack(spacing: 0) {
                        if !isHandleHidden {
                            Deal with()
                        }
                        self.content material()
                        Spacer()
                    }
                }
                .body(width: UIScreen.screenWidth, peak: UIScreen.screenHeight)
                .background(backgroundColor != nil ? backgroundColor! : Colour.black)
                .cornerRadius(withCorners ? 40.0 : 0)
                .shadow(shade: Colour(.sRGBLinear, white: 0, opacity: 0.13), radius: 10.0)
                .offset(y: place(from: place) + dragState.translation.peak)
                .animation(dragState.isDragging ? nil : .interpolatingSpring(stiffness: 300.0, damping: 30.0, initialVelocity: 10.0), worth: UUID())
                .edgesIgnoringSafeArea([.top, .bottom])
                .onTapGesture {}
                .onChange(of: place) { _ in
                    withAnimation(.easeInOut) {
                        highlightedBackground.toggle()
                    }
                }
            )
            .onTapGesture {
                place = place == .backside ? .high : .backside
            }
    }


    non-public func place(from cardPosition: SlideCardPosition) -> CGFloat {
        change cardPosition {
        case .high: return UIScreen.screenHeight - contentHeight - UIScreen.topSafeAreaHeight
        case .backside: return 1000
        }
    }
}

enum SlideCardPosition {
    case high
    case backside
}

non-public enum SlideDragState {
    case inactive
    case dragging(translation: CGSize)

    var translation: CGSize {
        change self {
        case .inactive:
            return .zero
        case .dragging(let translation):
            return translation
        }
    }

    var isDragging: Bool {
        change self {
        case .inactive:
            return false
        case .dragging:
            return true
        }
    }
}

non-public struct Deal with: View {
    non-public let handleThickness: CGFloat = 5
    var physique: some View {
        RoundedRectangle(cornerRadius: handleThickness / 2.0)
            .body(width: 34, peak: handleThickness)
            .foregroundColor(.white)
            .padding(.high, 8)
    }
}
import UIKit

extension UIScreen {
    static let screenWidth = UIScreen.foremost.bounds.measurement.width
    static let screenHeight = UIScreen.foremost.bounds.measurement.peak
    static let screenSize = UIScreen.foremost.bounds.measurement
    non-public static let window = UIApplication.shared.home windows[0]
    non-public static let safeFrame = window.safeAreaLayoutGuide.layoutFrame

    static var topSafeAreaHeight: CGFloat {
        safeFrame.minY
    }

    static var bottomSafeAreaHeight: CGFloat {
        window.body.maxY - safeFrame.maxY
    }
}
import SwiftUI

struct PopUpUIView: View {

    @Binding var shouldShow: SlideCardPosition
    @State var text1 = "some random textual content"

    var physique: some View {
        SlideOverCard(place: $shouldShow,
                      contentHeight: 300) {
            VStack(spacing: 10) {
                Textual content(text1)
                    .foregroundColor(.white)
                    .padding(.high, 80)
            }
        }.onChange(of: shouldShow) { _ in
            if shouldShow == .high {
                text1 = UUID().uuidString
            }
        }
    }
}

struct PopUpUIView_Previews: PreviewProvider {
    static var previews: some View {
        PopUpUIView(shouldShow: .fixed(.backside))
    }
}

Instance of incorrect animation with a dynamic textual content.

enter image description here

Instance of what I wish to obtain. It’s working tremendous if textual content is static.

enter image description here

Leave a Reply