MyProgressBar2 📦

我自己設計的進度條 (不包含標題的部分),可設定內距 (padding)、外距 (margin) 與進度條高度。

import SwiftUI

/// ⭐️ `NeuProgressBar2` 的「進度條」部分
public struct MyProgressBar2<Bar: View>: View {
    
    let height : CGFloat  // 進度條高度
    let padding: CGFloat  // 進度條與凹槽邊緣的間距
    let margin : CGFloat  // 凹槽上下邊緣與外界的間距
    let bar    : Bar      // 進度條的樣式
    
    @Binding var percent: CGFloat  // 0.0 ~ 1.0
    
    public init(
        _ percent: Binding<CGFloat>, 
        height   : CGFloat = 10, 
        padding  : CGFloat = 2, 
        margin   : CGFloat = 0,
        @ViewBuilder bar: () -> Bar  // ⭐️ @ViewBuilder closure
    ) {
        self._percent = percent      // ⭐️ 傳進 @Binding 變數
        self.height   = height
        self.padding  = padding
        self.margin   = margin
        self.bar      = bar()
    }
    
    public var body: some View {
        
        let h = height
        let p = padding
        let m = margin
        
        // ⭐️ 為了計算進度條「著色部分」的寬度,使用 `GeometryReader`
        return GeometryReader { geometry in
            
            ZStack(alignment: .leading) {
                
                // 凹槽
                Capsule()
                    .frame(height: h + 2 * p)  // ⭐️ 定義凹槽的「高度」
                    .foregroundColor(Neu.color.cardBackground)
                    // 凹槽漸層效果 (groove gradient)
                    .overlay(Neu.progressBarGroove.opacity(0.7))  // ⭐️ 讓背景再透過來一些
                    .clipShape(Capsule())
                
                // 進度條
                self.bar               // ⭐️ 進度條「樣式」
                    .frame(height: h)  // ⭐️ 進度條「高度」
                    .mask(             // ⭐️ 將進度條依「下面的形狀」剪下來
                        Capsule()      // ⭐️ 進度條「形狀」
                            // ⭐️ 計算進度條「後面空白部分的寬度」
                            .padding(.trailing, (geometry.size.width - 2 * p) * (1 - self.percent))
                            // ⭐️ 進度條前後各再扣掉一個 padding
                            .padding(.horizontal, p)  
                ) // mask
                    .shadow(color: Neu.color.text.opacity(0.7), radius: 2, x: 0, y: 1)
                
            } // ZStack
            
        }// container (GeometryReader)
            .frame(height: h + 2 * p + 2 * m)         // ⭐️ 計算「總高度」
    }
}

// ⭐️ 給進度條「預設樣式」
extension MyProgressBar2 where Bar == LinearGradient {
    public init(
        _ percent: Binding<CGFloat>, 
        height   : CGFloat = 10, 
        padding  : CGFloat = 2, 
        margin   : CGFloat = 0
    ) {
        self._percent = percent  // ⭐️ 傳進 @Binding 變數的方式
        self.height   = height
        self.padding  = padding
        self.margin   = margin
        // ⭐️ 給進度條「預設樣式」
        self.bar      = Gradient.right(Color.yellow.opacity(0.8), .red) as! Bar
    }
}

Last updated