SwiftUI自定义Picker问题:如何设置默认起始位置为0,00
解决SwiftUI自定义Picker默认显示0,00的问题
你的基于UIScrollView的方案完全可以实现默认显示0,00的需求,只需要调整偏移量的初始化和绑定逻辑即可,具体修改步骤如下:
- 拆分偏移量绑定:原代码中两个滑块共用一个
offset变量导致滚动同步,现在为整数和小数滑块分别设置独立的偏移量变量。 - 计算整数部分初始偏移:找到
0在整数数组中的索引,将初始偏移量设置为索引 × 单个元素高度(90),确保0显示在中间位置。 - 同步偏移量到ScrollView:在
CustomSlider的updateUIView方法中,将绑定的偏移量同步到UIScrollView的contentOffset,保证初始位置生效。 - 小数部分初始偏移:小数部分默认显示第一个元素"00",对应初始偏移量为0。
修改后的完整代码
import SwiftUI struct CustomPicker: View { @State var integerOffset: CGFloat @State var doubleOffset: CGFloat = 0 let integers: [Int] let doubles: [String] init(integers: [Int], doubles: [String]) { self.integers = integers self.doubles = doubles // 计算0在整数数组中的索引,设置初始偏移 if let startIndex = integers.firstIndex(of: 0) { self._integerOffset = State(initialValue: CGFloat(startIndex) * 90) } else { self._integerOffset = State(initialValue: 0) } } var body: some View { VStack { HStack(spacing: 0) { CustomSlider(sliderItems: integers.count, offset: $integerOffset, content: { VStack(spacing: 0){ ForEach(integers, id: \.self ){ index in VStack { Text("\(index)") .font(.custom("Calibre", size: 65)) .foregroundColor(.black) .frame(maxWidth: 90, maxHeight: 90) }.onTapGesture { print("DEBUG: \(index)") } } } }) .frame(maxWidth: 90 , maxHeight: 270) Text(",") .font(.custom("Calibre", size: 65)) CustomSlider(sliderItems: doubles.count, offset: $doubleOffset, content: { VStack(spacing: 0){ ForEach(doubles, id: \.self ){ index in VStack { Text("\(index)") .font(.custom("Calibre", size: 65)) .foregroundColor(.black) .frame(maxWidth: 90, maxHeight: 90) }.onTapGesture { print("DEBUG: \(index)") } } } }) .frame(maxWidth: 90 , maxHeight: 270) } } } } func getRect()-> CGRect{ return UIScreen.main.bounds } struct CustomSlider<Content: View> : UIViewRepresentable { var content: Content @Binding var offset : CGFloat var sliderItems: Int init(sliderItems: Int, offset: Binding<CGFloat>,@ViewBuilder content: @escaping ()->Content){ self.content = content() self._offset = offset self.sliderItems = sliderItems } func makeCoordinator() -> Coordinator { return CustomSlider.Coordinator(parent: self) } func makeUIView(context: Context) -> UIScrollView { let scrollView = UIScrollView() let swiftUIView = UIHostingController(rootView: content).view! let height = CGFloat(sliderItems * 90) + 180 swiftUIView.frame = CGRect(x: 0, y: 0, width: 90, height: height) scrollView.contentSize = swiftUIView.frame.size scrollView.addSubview(swiftUIView) scrollView.bounces = false scrollView.showsVerticalScrollIndicator = false scrollView.showsHorizontalScrollIndicator = false scrollView.delegate = context.coordinator // 设置初始偏移 scrollView.contentOffset.y = offset return scrollView } func updateUIView(_ uiView: UIScrollView, context: Context) { // 避免循环更新,仅当偏移不一致时设置 if abs(uiView.contentOffset.y - offset) > 1 { uiView.setContentOffset(CGPoint(x: 0, y: offset), animated: false) } } class Coordinator: NSObject, UIScrollViewDelegate{ var parent: CustomSlider init(parent: CustomSlider){ self.parent = parent } func scrollViewDidScroll(_ scrollView: UIScrollView ){ parent.offset = scrollView.contentOffset.y } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { let offset = scrollView.contentOffset.y let value = (offset / 90).rounded(.toNearestOrAwayFromZero) scrollView.setContentOffset(CGPoint(x: 0, y: value * 90), animated: false) } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { if !decelerate { let offset = scrollView.contentOffset.y let value = (offset / 90).rounded(.toNearestOrAwayFromZero) scrollView.setContentOffset(CGPoint(x: 0, y: value * 90), animated: false) } } } } struct CustomPicker_Previews: PreviewProvider { static var previews: some View { CustomPicker(integers: Array(-5...20), doubles: ["00", "25", "50", "75"]) } }
关键修改说明
- 在
CustomPicker的初始化方法中,通过firstIndex(of: 0)找到0在整数数组中的位置,计算出对应的初始偏移量,确保0默认显示在中间。 - 为整数和小数滑块分别设置独立的偏移量变量
integerOffset和doubleOffset,避免滚动同步干扰。 - 在
CustomSlider的makeUIView和updateUIView中,确保初始偏移量正确应用到UIScrollView,并且在偏移量变化时同步更新。
内容的提问来源于stack exchange,提问作者Alex Anghel
相关产品推荐
相关产品推荐

