SwiftUI自定义模式视图过渡滞后

时间:2020-07-07 20:55:19

标签: ios swift animation swiftui

我有一个自定义的模式视图,它是ZStack的一部分,启用后会覆盖其他内容。

当按下按钮时,我希望模式表从设备的底部边缘过渡到屏幕的中心,这已经有些完成了。但是,如在provided video中所见,在关闭模式视图时,动画有些失败,而且我很难弄清为什么。

我正在使用的模式视图的动画是

.animation(Animation.spring().speed(1.5))
.transition(.move(edge: .bottom))

为了完整起见,这是我的模态视图:

struct AddEventView: View {
    @State var eventName: String = ""
    @State var endDate = Date().addingTimeInterval(60)
    @State var gradientIndex: Int = 0
    
    @EnvironmentObject var model: Model
    
    let existingEvent: Event?
    
    let linearGradients: [LinearGradient] = Gradient.gradients.map {
        LinearGradient(
            gradient: $0,
            startPoint: .topTrailing,
            endPoint: .bottomLeading
        )
    }
    
    /// This closure is invoked when the view is dimissed, with a newly created Event passed as its parameter.
    /// If the user cancelled this action, `nil` is passed as the parameter
    let onDismiss: (Event?) -> Void
    
    var body: some View {
        print("Redrawing AddEventView")
        return VStack(spacing: 30.0) {
            HStack {
                Spacer().frame(width: 44)
                
                Spacer()
                
                Text(existingEvent == nil ? "Create Event" : "Edit Event")
                    .font(.title3)
                    .bold()
                
                Spacer()
                
                Button(action: {
                    onDismiss(nil)
                }) {
                    Image(systemName: "xmark.circle.fill")
                        .imageScale(.large)
                }
                .frame(width: 44)
            }
            .padding(.bottom, 5)
            .padding(.top, 8)
            
            HStack {
                Text("Name of Event").padding(.trailing, 20)
                
                TextField("My Birthday", text: $eventName)
                    .frame(height: 35)
            }
                        
            DatePicker(
                "Date of Event".padding(toLength: 19, withPad: " ", startingAt: 0),
                selection: $endDate,
                in: Date()...
            )
            .frame(height: 35)
            
            ColorChooser(
                linearGradients,
                selectedIndex: $gradientIndex
            )
            .frame(height: 75)
                        
            Button(action: {
                let adjustedEnd = Calendar.current.date(bySetting: .second, value: 0, of: endDate)
                
                let event = Event(
                    name: eventName,
                    start: existingEvent?.start ?? Date(),
                    end: adjustedEnd!,
                    gradientIndex: gradientIndex
                )
                onDismiss(event)
            }) {
                RoundedRectangle(cornerRadius: 13)
                    .frame(maxWidth: .infinity)
                    .frame(height: 42)
                    .overlay(
                        Text(existingEvent == nil ? "Add Event" : "Edit Event")
                            .foregroundColor(.white)
                            .bold()
                    )
                    .padding(.horizontal, 1)
            }
            .padding(.top, 8)
            .disabled(self.eventName.isEmpty)
        }
        .padding(.all, 16)
        .background(Color.white)
        .cornerRadius(16)
        .shadow(radius: 16)
        .onAppear {
            if let event = existingEvent {
                self.eventName = event.name
                self.endDate = event.end
                self.gradientIndex = event.gradientIndex
            }
        }
    }

}

和我的ContentView:

struct ContentView: View {
    @State var progress: Double = 0.0
    @State var showModal: Bool = false
    @State var showPopover: Bool = false
    
    @State var modifiableEvent: Event?
    @State var now: Date = Date()
    @State var confettiView = ConfettiUIView()
        
    @EnvironmentObject var model: Model
    
    let timer = Timer.publish(every: 1, on: .current, in: .common).autoconnect()
    let columns: [GridItem] = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2)
    
    var alertButtons: [Alert.Button] {
        return Model.SortableKeyPaths.map { key, _ in
            .default(Text(key)) { model.sortedKey = key }
        }
    }
    
    func onEventEnd() {
        self.confettiView.emit(with: [.text("?")])
        AudioManager.shared.play("Success 1.mp4")
        
        let taptics = UINotificationFeedbackGenerator()
        taptics.notificationOccurred(.success)
    }
    
    var grid: some View {
        LazyVGrid(columns: columns, spacing: 10) {
            ForEach(model.events, id: \.self) { event in
                SmallCardView(event: event)
                    .contextMenu {
                        Button(action: {
                            modifiableEvent = event
                            withAnimation {
                                self.showModal = true
                            }
                        }) {
                            Text("Edit")
                            Image(systemName: "slider.horizontal.3")
                        }
                        
                        Button(action: {
                            model.removeEvent(event)
                        }) {
                            Text("Delete")
                            Image(systemName: "trash")
                        }
                    }
                    .animation(.linear)
            }
            
            if !showModal || modifiableEvent != nil {
                AddEventButtonView() {
                    modifiableEvent = nil
                    self.showModal = true
                }
            } else {
                Spacer().frame(height: 100)
            }
        }
        .navigationBarTitle(Text("My Events"), displayMode: .large)
        .navigationBarItems(
            leading: Button(action: { }) {
                Image(systemName: "ellipsis")
                    .imageScale(.large)
            },
            trailing: Button(action: { self.showPopover = true }) {
                Image(systemName: "arrow.up.arrow.down").imageScale(.large)
            }
            .actionSheet(isPresented: $showPopover) {
                ActionSheet(
                    title: Text("Sort Events"),
                    buttons: alertButtons + [.cancel()]
                )
            }
        )
    }
    
    var body: some View {
        return ZStack {
            NavigationView {
                ScrollView {
                    grid.padding(.horizontal, 16)
                }
                .padding(.top)
            }
            .brightness(self.showModal ? -0.1 : 0)
            .blur(radius: self.showModal ? 16 : 0)
            .scaleEffect(self.showModal ? 0.95 : 1)
            
            if self.showModal {
                AddEventView(existingEvent: modifiableEvent) { event in
                    if let event = event {
                        self.model.removeEvent(modifiableEvent)
                        self.model.addEvent(event)
                    }
                    
                    withAnimation {
                        self.showModal = false
                    }
                }
                .padding(.horizontal, 16)
                .zIndex(1.0)
                .animation(Animation.spring().speed(1.5))
                .transition(.move(edge: .bottom))
            }
        
            EmptyView().id("\(self.now.hashValue)")
        }
        .overlay(
           UIViewWrapper(view: $confettiView)
            .edgesIgnoringSafeArea(.all)
            .allowsHitTesting(false)
        )
        .onReceive(timer) { _ in
            if !showModal { self.now = Date() }

            if model.events.contains(where: { -1...0 ~= $0.timeRemaining }) {
                onEventEnd()
            }
        }
    }
}

0 个答案:

没有答案