Saturday, September 24, 2022
HomeiOS Developmentswift - SwiftUI. Subview animation just isn't working if subview's View @State...

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


I’ve a construction, the place the father or mother view is a hidden pop-up with content material. On some person motion father or mother View begins to slip up. I want all its content material to slip up with the father or mother View. It was working completely earlier than iOS 16, however now it is damaged. If the kid’s View subview @State is modified through the animation, then this View seems immediately on a display, i.e. not sliding. As I perceive, as a result of View’s @State was modified SwiftUI redraws this explicit View and disables its animation, so it seems in its ultimate place with out animation. I used to be making an attempt to drive the animation of this explicit View utilizing .animation or withAnimation. Nothing of it helped. The way 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 = .prime
            } label: {
                Textual content("Present PopUp")
                    .padding(.prime, 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(colour: 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 ? .prime : .backside
            }
    }


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

enum SlideCardPosition {
    case prime
    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(.prime, 8)
    }
}
import UIKit

extension UIScreen {
    static let screenWidth = UIScreen.foremost.bounds.dimension.width
    static let screenHeight = UIScreen.foremost.bounds.dimension.peak
    static let screenSize = UIScreen.foremost.bounds.dimension
    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(.prime, 80)
            }
        }.onChange(of: shouldShow) { _ in
            if shouldShow == .prime {
                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 high quality if textual content is static.

enter image description here

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments