SwiftUI中如何从自定义时间选择器获取计算值?
解决SwiftUI多滚轮时间选择器的@Binding同步问题
问题现状
你已经实现了多滚轮时间选择器,内部通过selectedTimeAsString计算属性生成选中的时间字符串,但无法让父视图获取到这个值。虽然声明了@Binding var time: String,但未将计算属性的结果同步到该绑定变量中。
简洁解决方案
无需额外创建模型,只需监听滚轮数字的变化,自动将计算出的时间字符串同步到@Binding变量即可,具体步骤如下:
- 添加数字变化监听
在HoursPickerView的body中,给HStack添加.onChange修饰器,监听所有滚轮数字的变化,一旦有变动就更新time绑定变量:
struct HoursPickerView: View { @State private var hoursFirstDigit = 0 @State private var hoursSecondDigit = 0 @State private var hoursThirdDigit = 0 @State private var hoursForthDigit = 0 @State private var hoursLastDigit = 0 @State private var minutesFirstDigit = 0 @State private var minutesLastDigit = 0 @Binding var time : String private var selectedTimeAsString : String { let hours = [hoursFirstDigit,hoursSecondDigit,hoursThirdDigit,hoursForthDigit,hoursLastDigit].map{"\($0)"} let minutes = [minutesFirstDigit,minutesLastDigit].map{"\($0)"} let hoursJoined = hours.joined() let minutesJoined = minutes.joined() return hoursJoined + ":" + minutesJoined } var body: some View { VStack { HStack(spacing : -15){ HoursPickerColumn(range: 0...9, selection: $hoursFirstDigit) HoursPickerColumn(range: 0...9, selection: $hoursSecondDigit) HoursPickerColumn(range: 0...9, selection: $hoursThirdDigit) HoursPickerColumn(range: 0...9, selection: $hoursForthDigit) HoursPickerColumn(range: 0...9, selection: $hoursLastDigit) Text(":") .font(.title) .padding(.horizontal) HoursPickerColumn(range: 0...5, selection: $minutesFirstDigit) HoursPickerColumn(range: 0...9, selection: $minutesLastDigit) } .frame(width: 280, height: 50) .clipped() .background( RoundedRectangle(cornerRadius: 10) .foregroundStyle(.white.shadow(.inner(color: .gray, radius: 1))) ) // 监听所有滚轮数字变化,同步更新绑定的time变量 .onChange(of: [hoursFirstDigit, hoursSecondDigit, hoursThirdDigit, hoursForthDigit, hoursLastDigit, minutesFirstDigit, minutesLastDigit]) { _ in time = selectedTimeAsString } Text(selectedTimeAsString).bold() } } }
- 父视图使用方式
在父视图中声明@State变量存储时间,绑定到HoursPickerView的time参数,即可实时获取选中的时间字符串:
struct ParentView: View { @State private var selectedTime = "00000:00" var body: some View { VStack { HoursPickerView(time: $selectedTime) Text("父视图获取的时间:\(selectedTime)") } } }
可选优化:初始化同步初始时间
如果需要让滚轮初始化时显示父视图传入的初始时间,可添加初始化方法,将time字符串拆分到各个digit变量中:
init(time: Binding<String>) { self._time = time // 解析初始时间字符串,设置各个digit的初始值 let components = time.wrappedValue.split(separator: ":") if let hoursStr = components.first, hoursStr.count == 5 { hoursFirstDigit = Int(String(hoursStr[hoursStr.startIndex])) ?? 0 hoursSecondDigit = Int(String(hoursStr[hoursStr.index(after: hoursStr.startIndex)])) ?? 0 hoursThirdDigit = Int(String(hoursStr[hoursStr.index(hoursStr.startIndex, offsetBy: 2)])) ?? 0 hoursForthDigit = Int(String(hoursStr[hoursStr.index(hoursStr.startIndex, offsetBy: 3)])) ?? 0 hoursLastDigit = Int(String(hoursStr[hoursStr.index(hoursStr.startIndex, offsetBy: 4)])) ?? 0 } if let minutesStr = components.last, minutesStr.count == 2 { minutesFirstDigit = Int(String(minutesStr[minutesStr.startIndex])) ?? 0 minutesLastDigit = Int(String(minutesStr[minutesStr.index(after: minutesStr.startIndex)])) ?? 0 } }
内容的提问来源于stack exchange,提问作者OverD
相关产品推荐
相关产品推荐

