我有带动画的按钮:
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)")
}
}
}
}
我的按钮是这样工作的:
我需要实现这个目标:
这是 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 上运行?
您的录制按钮正在累积动画。要解决此问题,您可以在录制停止时切换到新对象(通过更改其 ID)。
这样做:
@State var
。.id(cid)
添加到圆圈中。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
}
}
}