簡體   English   中英

如何自己實現同一個 iOS 16 鎖屏圓形小部件?

[英]How to implement the same iOS 16 lock screen circular widget myself?

我正在嘗試自己實現鎖屏小部件

我目前實現的小部件

我目前實現的小部件

我想實現這個ios16鎖屏小部件

我幾乎什么都做了,就是沒能實現小圓圈的透明邊框。 我找不到使它后面的環的背景變得透明的方法。

我的代碼

struct RingTipShape: Shape { // small circle
    var currentPercentage: Double
    var thickness: CGFloat

    func path(in rect: CGRect) -> Path {
        var path = Path()

        let angle = CGFloat((240 * currentPercentage) * .pi / 180)
        let controlRadius: CGFloat = rect.width / 2 - thickness / 2
        let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
        let x = center.x + controlRadius * cos(angle)
        let y = center.y + controlRadius * sin(angle)
        let pointCenter = CGPoint(x: x, y: y)
        path.addEllipse(in:
            CGRect(
                x: pointCenter.x - thickness / 2,
                y: pointCenter.y - thickness / 2,
                width: thickness,
                height: thickness
            )
        )
        return path
    }
    
    var animatableData: Double {
        get { return currentPercentage }
        set { currentPercentage = newValue }
    }
}
struct RingShape: Shape {
    var currentPercentage: Double
    var thickness: CGFloat
    
    func path(in rect: CGRect) -> Path {
        var path = Path()
        
        path.addArc(center: CGPoint(x: rect.width / 2, y: rect.height / 2), radius: rect.width / 2 - (thickness / 2), startAngle: Angle(degrees: 0), endAngle: Angle(degrees: currentPercentage * 240), clockwise: false)
        
        return path.strokedPath(.init(lineWidth: thickness, lineCap: .round, lineJoin: .round))
    }
    var animatableData: Double {
        get { return currentPercentage}
        set { currentPercentage = newValue}
    }
}
struct CircularWidgetView: View { // My customizing widget view
    @State var percentage: Double = 1.0
    
    var body: some View {
    
        GeometryReader { geo in
            ZStack {
                RingBackgroundShape(thickness: 5.5)
                    .rotationEffect(Angle(degrees: 150))
                    .frame(width: geo.size.width, height: geo.size.height)
                    .foregroundColor(.white.opacity(0.21))
                RingShape(currentPercentage: 0.5, thickness: 5.5)
                    .rotationEffect(Angle(degrees: 150))
                    .frame(width: geo.size.width, height: geo.size.height)
                    .foregroundColor(.white.opacity(0.385))
                RingTipShape(currentPercentage: 0.5, thickness: 5.5)
                    .rotationEffect(Angle(degrees: 150))
                    .frame(width: geo.size.width, height: geo.size.height)
                    .foregroundColor(.white)
                    /* 
                    I want to make RingTipShape completely
                    transparent. Ignoring even the RingShape behind it
                    */
   
                VStack(spacing: 4) {
                    Image(systemName: "scooter")
                        .resizable()
                        .frame(width: 24, height: 24)
                    Text("hello")
                        .font(.system(size: 10, weight: .semibold))
                        .lineLimit(1)
                        .minimumScaleFactor(0.1)
                }
            }
        }
    }
}

如何制作透明邊框,同時忽略其背后視圖的背景?

這是一個很好的練習。 缺少的部分是面具。

注意:盡管有很多方法可以改進現有代碼,但我會盡量堅持原來的解決方案,因為重點是通過實踐獲得經驗(基於評論)。 不過,我會在最后分享一些技巧。

所以我們可以分兩步來思考:

  1. 我們需要一些方法來制作另一個RingTipShape與我們現有的相同(居中)position 但更大一點。
  2. 我們需要找到一種方法來創建一個僅從其他內容中刪除該形狀的蒙版(在我們的例子中是軌道環)

第一點很簡單,我們只需要定義外部厚度,以便將橢圓放置在軌道頂部的正確位置:

struct RingTipShape: Shape { // small circle
    //...
    let outerThickness: CGFloat
    //...
    let controlRadius: CGFloat = rect.width / 2 - outerThickness / 2
    //...
}

然后我們現有的代碼更改為:

RingTipShape(currentPercentage: percentage, thickness: 5.5, outerThickness: 5.5)

現在對於第二部分,我們需要一些東西來創建一個更大的圓,這很容易:

RingTipShape(currentPercentage: percentage, thickness: 10.0, outerThickness: 5.5)

好的,現在是最后一部分,我們將使用這個(更大的)形狀來創建一種倒置的蒙版:

private var thumbMask: some View {
    ZStack {
        Color.white // This part will be transparent
        RingTipShape(currentPercentage: percentage, thickness: 10.0, outerThickness: 5.5)
            .fill(Color.black) // This will be masked out
            .rotationEffect(Angle(degrees: 150))
    }
    .compositingGroup() // Rasterize the shape
    .luminanceToAlpha() // Map luminance to alpha values
}

我們像這樣應用面具:

RingShape(currentPercentage: percentage, thickness: 5.5)
    .rotationEffect(Angle(degrees: 150))
    .foregroundColor(.white.opacity(0.385))
    .mask(thumbMask)

結果是:


一些觀察/提示:

  1. 您不需要CircularWidgetView中的GeometryReader (和所有框架修改器), ZStack將為視圖提供所有可用空間。
  2. 您可以將.aspectRatio(contentMode: .fit)添加到圖像中以避免拉伸。
  3. 您可以利用現有的 api 來制作您的軌道形狀。

例如:

struct MyGauge: View {

    let value: Double = 0.5
    let range = 0.1...0.9

    var body: some View {
        ZStack {
            // Backing track
            track().opacity(0.2)

            // Value track
            track(showsProgress: true)
        }
    }

    private var mappedValue: Double {
        (range.upperBound + range.lowerBound) * value
    }

    private func track(showsProgress: Bool = false) -> some View {
        Circle()
            .trim(from: range.lowerBound, to: showsProgress ? mappedValue : range.upperBound)
            .stroke(.white, style: .init(lineWidth: 5.5, lineCap: .round))
            .rotationEffect(.radians(Double.pi / 2))
    }
}

會導致:

這通過使用trim修飾符簡化了一些事情。

我希望這是有道理的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM