MST

星途 面试题库

面试题:Swift中如何在SwiftUI里创建一个自定义的交互式动画过渡效果

创建一个SwiftUI应用,其中有两个视图A和B。用户通过手势(如拖动)在A和B之间进行过渡,过渡效果自定义为根据手势的移动距离和方向来动态改变视图的旋转、缩放和透明度。详细阐述如何利用SwiftUI的手势识别、动画和过渡API来实现这一复杂的交互式动画过渡效果,并且说明如何处理手势取消、完成等不同状态下的动画逻辑。
22.8万 热度难度
编程语言Swift

知识考点

AI 面试

面试题答案

一键面试
  1. 项目初始化
    • 创建一个新的SwiftUI项目。在ContentView.swift中定义两个视图ViewAViewB
    struct ViewA: View {
        var body: some View {
            Text("View A")
               .padding()
               .background(Color.blue)
               .foregroundColor(.white)
               .cornerRadius(10)
        }
    }
    
    struct ViewB: View {
        var body: some View {
            Text("View B")
               .padding()
               .background(Color.green)
               .foregroundColor(.white)
               .cornerRadius(10)
        }
    }
    
  2. 手势识别
    • 使用DragGesture来识别用户的拖动手势。在ContentView中添加一个状态变量来跟踪手势的偏移量。
    struct ContentView: View {
        @State private var dragOffset = CGSize.zero
        var body: some View {
            let drag = DragGesture()
               .onChanged { value in
                    self.dragOffset = value.translation
                }
               .onEnded { value in
                    self.dragOffset = CGSize.zero
                }
            return ZStack {
                ViewB()
                   .offset(x: dragOffset.width, y: dragOffset.height)
                   .scaleEffect(1 + abs(dragOffset.width) / 200)
                   .rotationEffect(.degrees(Double(dragOffset.width) / 5))
                   .opacity(1 - abs(dragOffset.width) / 200)
                ViewA()
                   .gesture(drag)
            }
        }
    }
    
    • onChanged闭包中,更新dragOffset,这个值会用于后续的动画效果。在onEnded闭包中,将dragOffset重置为(0, 0),以准备下一次手势操作。
  3. 动画和过渡效果
    • 通过offsetscaleEffectrotationEffectopacity修饰符,根据dragOffset的值来动态改变视图的位置、缩放、旋转和透明度。
    • 例如,scaleEffect(1 + abs(dragOffset.width) / 200)会根据手势的水平偏移量来缩放视图,rotationEffect(.degrees(Double(dragOffset.width) / 5))根据水平偏移量旋转视图,opacity(1 - abs(dragOffset.width) / 200)根据水平偏移量改变透明度。
  4. 处理手势取消和完成状态下的动画逻辑
    • 手势取消:当用户取消手势(例如,中途抬起手指但没有完全结束拖动),可以使用DragGestureonCancelled闭包。在这个闭包中,可以通过动画将视图恢复到初始状态。
    let drag = DragGesture()
       .onChanged { value in
            self.dragOffset = value.translation
        }
       .onEnded { value in
            self.dragOffset = CGSize.zero
        }
       .onCancelled {
            withAnimation {
                self.dragOffset = CGSize.zero
            }
        }
    
    • 手势完成:在onEnded闭包中,已经实现了将dragOffset重置为(0, 0),视图会自动过渡回初始状态。可以在此处添加更复杂的逻辑,比如根据最终的偏移量判断是否要执行特定的操作,或者执行更平滑的过渡动画。例如,如果偏移量超过某个阈值,可以执行一个更快速的过渡动画回到初始状态。
    let drag = DragGesture()
       .onChanged { value in
            self.dragOffset = value.translation
        }
       .onEnded { value in
            if abs(value.translation.width) > 100 {
                withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
                    self.dragOffset = CGSize.zero
                }
            } else {
                withAnimation {
                    self.dragOffset = CGSize.zero
                }
            }
        }
    

通过上述步骤,可以利用SwiftUI的手势识别、动画和过渡API实现复杂的交互式动画过渡效果,并处理好手势取消、完成等不同状态下的动画逻辑。