年底各种活动,今天也是刚参与了一次幸运大转盘抽奖,可惜手气不佳,于是使用 SwiftUI
也来制作一个转盘抽奖示例
演示效果
关键代码示例
//
// Created by codeun.com on 25/10/24.
//
import SwiftUI
import Combine
// 用于管理轮盘的状态和行为
class RouletteViewModel: ObservableObject {
// @Published属性:当这些属性改变时,UI会自动刷新
@Published var segmentCount = 1 // 当前轮盘分段的数量
@Published var rotation: Double = 0 // 当前旋转角度,用于动画效果
@Published var isSpinning = false // 是否正在旋转
@Published var winningName: String = "" // 中奖项的名称
@Published var showAlert = false // 是否显示中奖提示
@Published var usedColors: [Color] = [.blue] // 已经使用过的颜色
@Published var colors: [Color] = [.black.opacity(0.35)] // 轮盘颜色数组
@Published var usedColorsNames: [Color] = [.blue] // 已使用的颜色名称数组
@Published var names: [String] = [""] // 存储每个分段的名称
@Published var winningColor: [String] = [] // 存储中奖分段的颜色
@Published var newColorName: String = "" // 新增分段的颜色名称
// 非Published属性:不会自动刷新UI
var selectedColor: Color = .blue // 当前选中的颜色
var lastUsedColor: Color = .clear // 上一个使用的颜色
let availableColors: [Color] = [.red.opacity(0.85), .blue, .green, .yellow, .purple, .orange, .brown, .cyan, .teal, .indigo] // 可用的颜色集合
let totalSpinDuration: Double = 5.0 // 旋转总持续时间
var totalRotations: Double = 3500 // 总旋转角度,模拟多圈转动
// 轮盘旋转方法
func spinRoulette() {
// 确保不在旋转中时才执行旋转逻辑
guard !isSpinning else { return }
isSpinning = true
// 使用withAnimation对旋转进行动画效果
withAnimation(Animation.timingCurve(0.1, 0.8, 0.3, 1.0, duration: totalSpinDuration)) {
rotation += totalRotations // 增加旋转角度,触发旋转动画
}
// 旋转结束后执行的逻辑
DispatchQueue.main.asyncAfter(deadline: .now() + totalSpinDuration) {[weak self] in
guard let this = self else { return }
this.isSpinning = false // 旋转完成,重置状态
// 计算当前旋转后的角度并确定中奖项
let incompleteRotation = Int(this.rotation) % 360
let restOfRotation: Double = Double(incompleteRotation) / (360.0 / Double(this.segmentCount))
let restOfRotationInteger = Int(restOfRotation)
let winningIndex = Double(restOfRotationInteger) == restOfRotation ? restOfRotationInteger : restOfRotationInteger + 1
// 确定中奖分段的颜色及名称
this.winningColor = this.names.reversed()
this.winningName = this.winningColor[winningIndex - 1]
this.showAlert = true // 显示中奖提示
}
}
// 添加新分段方法
func addNewItem() {
// 确保新分段的颜色名称不为空
guard !newColorName.isEmpty else { return }
addNewColorAndName(name: newColorName) // 添加新颜色和名称
names.removeAll(where: { $0 == "" }) // 移除空项
segmentCount = names.count // 更新分段数量
newColorName = "" // 清空新颜色名称
}
// 删除分段方法
func deleteItems(at offsets: IndexSet) {
names.remove(atOffsets: offsets) // 从名称数组中移除指定分段
segmentCount -= 1 // 更新分段数量
if names.isEmpty { // 若无剩余分段,则初始化为空项
names = [""]
segmentCount = 1
}
}
// 添加新颜色和名称方法
func addNewColorAndName(name: String) {
// 检查是否有可用的颜色未使用
if usedColors.count < availableColors.count {
let unusedColors = availableColors.filter { !usedColors.contains($0) }
if let randomColor = unusedColors.randomElement() { // 随机选择一个未使用颜色
colors.insert(randomColor, at: 0)
usedColors.append(randomColor)
lastUsedColor = randomColor // 更新最后使用的颜色
if availableColors.firstIndex(of: randomColor) != nil {
names.append(name) // 添加新名称到分段
}
}
} else {
// 若所有颜色已使用,从已使用颜色中随机选择一个新颜色
if let randomColor = availableColors.filter({ $0 != lastUsedColor && $0 != colors[0] }).randomElement() {
colors.append(randomColor)
lastUsedColor = randomColor // 更新最后使用的颜色
if availableColors.firstIndex(of: randomColor) != nil {
names.append(name) // 添加新名称到分段
}
}
}
}
}
完整代码下载
本文自 https://www.codeun.com 发布,相应代码均自主编写并严格审阅和测试,完整代码中包含丰富的学习笔记和使用方式、实用技巧。
· 如若转载,请注明出处:https://www.codeun.com/archives/1361.html ·