带有循环动画和状态 swiftUI 的按钮

问题描述 投票:0回答:1

我有带动画的按钮:

enum RecordState {
    case recording
    case notRecording
    case none
}

struct RecordButton: View {
    @State private var isRecording = false
    @Binding var recordState: RecordState
    var changedState: (RecordState) -> Void

    var body: some View {
        ZStack {
            Button {
                if isRecording {
                    withAnimation(nil) {
                        isRecording.toggle()
                    }
                } else {
                    withAnimation(.easeInOut(duration: 0.8)) {
                        isRecording.toggle()
                    }
                }
                changedState(isRecording ? .recording : .notRecording)
            } label: {
                Image(isRecording ? "ic_record_cancel" : "ic_record_start")
                    .resizable()
                    .frame(width: 76, height: 76)
            }
            .zIndex(1.0)
            
            Circle()
                .frame(width: 76, height: 76)
                .foregroundColor(Color(Const.colorNavBarItem))
                .scaleEffect(isRecording ? 1.4 : 1.0)
                .opacity(isRecording ? 0.0 : 1.0)
                .animation(
                    Animation.easeInOut(duration: 0.8)
                        .repeatForever(autoreverses: false)
                )
                .opacity(isRecording ? 1.0 : 0.0)
                .zIndex(0.1)
                .animation(nil, value: isRecording)
        }
    }
}

当我按下按钮时,recordState 从 .none 更改为 .recording,并且我看到动画。当我再次按下按钮时,recordState 更改为 .notRecording,并且我看不到动画。但是当我再次按下时,再次看到这个动画 - 动画变形了。相互叠加,无法正常工作。

我像这样测试按钮:

import SwiftUI

struct TestView: View {
    @State var recordState: RecordState = .none

    var body: some View {
        VStack {
            Spacer()
            RecordButton(recordState: $recordState) { state in
                recordState = state
                print("\(recordState)")
            }
        }
    }
}

我的按钮是这样工作的:

enter image description here

我需要实现这个目标:

enter image description here

这是 UIKit 中带有此动画的代码的一部分:

  private func startRecordEffectAnimation() {
        guard self.recordEffect.tag == 0 else {
            return
        }
        guard !self.recordEffect.isHidden else {
            self.recordEffect.tag = 0
            return
        }
        self.recordEffect.tag = 1
        self.recordEffect.frame = self.recordButton.frame
        self.recordEffect.alpha = 0.6
        let dx = self.recordEffect.frame.size.width / 3.0
        let dy = self.recordEffect.frame.size.height / 3.0
        UIView.animate(
            withDuration: 0.8,
            delay: 0.2,
            options: .curveLinear,
            animations: {
                self.recordEffect.frame.origin.x -= dx
                self.recordEffect.frame.origin.y -= dy
                self.recordEffect.frame.size.width += dx * 2
                self.recordEffect.frame.size.height += dy * 2
                self.recordEffect.alpha = 0.0
            },
            completion: { _ in
                self.recordEffect.tag = 0
                self.startRecordEffectAnimation()
            }
        )
    }
    
    private func updateRecordState() {
        switch recordState {
        case .isRecording:
            guard let cancelButtonImg = UIImage(named: "ic_record_cancel") else { return }
            recordButton.setImage(cancelButtonImg, for: .normal)
            recordEffect.isHidden = false
            startRecordEffectAnimation()
            SoundService.shared.startRecording()
        case .notRecording:
            guard let recordButtonImg = UIImage(named: "ic_record_start") else { return }
            recordButton.setImage(recordButtonImg, for: .normal)
            recordEffect.isHidden = true
            SoundService.shared.finishRecording(success: true)
        case .none:
            debugPrint("none record state")
        }
    }
   
    @objc private func tappedRecord(_ sender: CustomButton) {
        sender.isSelected = !sender.isSelected
        
        if sender.isSelected == true {
            recordState = .isRecording
            recordDelegate?.recordTapped(sender)
        } else if sender.isSelected == false  {
            recordState = .notRecording
            delegate?.tapped(sender)
            recordDelegate?.recordTapped(sender)
        }
    }
}

如何转换代码以使其在 swiftUI 上运行?

loops animation button swiftui state
1个回答
0
投票

您的录制按钮正在累积动画。要解决此问题,您可以在录制停止时切换到新对象(通过更改其 ID)。

这样做:

  1. 为圈子 ID 添加新的
    @State var
  2. .id(cid)
    添加到圆圈中。
  3. 设置录制停止时的 id
    cid = UUID()
struct RecordButton: View {
    @State private var isRecording = false
    @State private var cid = UUID()  // here
    @Binding var recordState: RecordState
    var changedState: (RecordState) -> Void
    
    var body: some View {
        ZStack {
            Button {
                if isRecording {
                    withAnimation(nil) {
                        isRecording.toggle()
                        cid = UUID()  // here
                    }
                } else {
                    withAnimation(.easeInOut(duration: 0.8)) {
                        isRecording.toggle()
                    }
                }
                changedState(isRecording ? .recording : .notRecording)
            } label: {
                Image(systemName: isRecording ? "globe" : "house")
                    .resizable()
                    .frame(width: 76, height: 76)
                    .animation(nil, value: isRecording)
                    
            }
            .zIndex(1.0)
            
            Circle()
                .frame(width: 76, height: 76)
                .foregroundColor(Color.green)
                .scaleEffect(isRecording ? 1.4 : 1.0)
                .opacity(isRecording ? 0.0 : 1.0)
                .animation(
                    Animation.easeInOut(duration: 0.8)
                        .repeatForever(autoreverses: false), value: isRecording
                )
                .opacity(isRecording ? 1.0 : 0.0)
                .zIndex(0.1)
                .animation(nil, value: isRecording)
                .id(cid)  // here
    
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.