You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI如何根据用户滚动调整视图旋转实现滚轮选择器效果

环形滚轮选择器数字偏转角度联动实现

当前实现效果

当前实现示例

目标实现效果

目标效果示例
核心要求:数字旋转角度与用户滚动操作实时联动,任意数字滚动到滚轮顶部位置时,保持正向正常显示状态。

现有实现代码

struct myVal : Equatable {
    let id = UUID()
    let val : String
}

enum Direction {
    case left
    case right
}

struct WheelView: View {
    // 圆环半径
    @State var radius : Double = 150
    // 滑动方向
    @State var direction = Direction.left
    // 圆环底部位置的数字索引
    @State var chosenIndex = 0
    // 圆环旋转角度
    @Binding var degree : Double
    let array : [myVal]
    let circleSize : Double
    
    var body: some View {
        ZStack {
            let anglePerCount = Double.pi * 2.0 / Double(array.count)
            let drag = DragGesture()
                .onEnded { value in
                    if value.startLocation.x > value.location.x + 10 {
                        direction = .left
                    } else if value.startLocation.x < value.location.x - 10 {
                        direction = .right
                    }
                   // 此处调用滚轮移动逻辑
                }
          // MARK: 滚轮容器开始
            ZStack {
                ForEach(0 ..< array.count) { index in
                    let angle = Double(index) * anglePerCount
                    let xOffset = CGFloat(radius * cos(angle))
                    let yOffset = CGFloat(radius * sin(angle))
                    Text("\(array[index].val)")
                        .rotationEffect(Angle(degrees: -degree))
                        .offset(x: xOffset, y: yOffset )
                }
            }
            .rotationEffect(Angle(degrees: degree))
            .gesture(drag)
            .onAppear() {
                radius = circleSize/2 - 30 // 30为内边距
            }
           // MARK: 滚轮容器结束
        }
    }
}

实现方案

现有逻辑的问题是给所有文本统一加了-degree的反向旋转,只能保证所有文字不跟随圆环整体转动,无法实现「文字随所在位置偏转、到达顶部自动摆正」的效果。
正确实现需要给每个条目单独计算旋转角度,逻辑如下:

  • 修正初始角度基准:默认三角函数的0角度对应圆弧3点钟方向,需要先偏移90度,把顶部作为0度基准位置。
  • 放弃全局统一反向旋转的逻辑,每个文字的旋转角度取自身当前位置相对于顶部的角度做反向旋转,就能保证:到达顶部的文字刚好正向,两侧文字随位置自然偏转。
  • 计算过程统一用弧度做运算,传给SwiftUI旋转方法时再对应转换,避免弧度、角度混算导致的角度错误。

核心代码修改

替换ForEach内部的布局逻辑,同时删除外层滚轮容器的.rotationEffect(Angle(degrees: degree))修饰符(角度已经计算到每个条目属性中,不需要再做整体旋转,避免角度叠加):

ForEach(0 ..< array.count) { index in
    // 计算单个条目的实时角度:基础角度偏移90度对齐顶部,叠加当前滚轮旋转量
    let itemAngle = Double(index) * anglePerCount - Double.pi/2 + (degree * .pi / 180)
    // 计算条目在圆环上的偏移坐标
    let xOffset = CGFloat(radius * cos(itemAngle))
    let yOffset = CGFloat(radius * sin(itemAngle))
    
    Text("\(array[index].val)")
        // 反向旋转条目自身相对于顶部的角度,保证顶部位置文字正向
        .rotationEffect(Angle(radians: -itemAngle))
        .offset(x: xOffset, y: yOffset)
}

可选优化点

  • 当前拖拽手势仅在结束时判断方向,没有跟手效果,可以把degree的更新逻辑移到拖拽的onChanged回调中,根据横向拖拽位移换算角度增量,滚动流畅度会明显提升。
  • 滚动结束后可以增加吸附逻辑,自动计算离顶部最近的条目,对齐到整格位置,更符合选择器的交互习惯。
  • 如果需要做两侧文字的大小、透明度渐变效果,可以直接根据itemAngle和顶部0角度的差值做线性映射,不需要额外计算位置。

内容的提问来源于stack exchange,提问作者jooh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:15:11