The Util Designer
SwiftUI 使用Capsule創建進度條
xcode 13.3.1, swift 5.5, iOS 15.4
2022-08-10
進度條在很多地方都會用到,這篇會介紹在SwiftUI 可以很簡單的做出進度條。
1. 使用Capsule創建一個高為20的白色膠囊形狀,並使用overlay疊加一個2個單位的灰色外框。
struct CapsuleProgressExample: View {
    
    var body: some View {
        Capsule()
            .fill(.white)
            .frame(height: 20)
            .overlay(
                Capsule()
                    .stroke(.gray, lineWidth: 2.0)
            )
    }
}
2. 再使用overlay在原有Capsule上面疊加一個綠色的長條。
struct CapsuleProgressExample: View {
    
    var body: some View {
        Capsule()
            .fill(.white)
            .frame(height: 20)
            .overlay(
                Capsule()
                    .stroke(.gray, lineWidth: 2.0)
            )
            .overlay(
                Capsule()
                    .inset(by: 1)
                    .fill(.green)
            )
    }
}
3. 疊加的綠色條是用來顯示當前進度的情況,我們可以用frame(width,height)來進行控制。
struct CapsuleProgressExample: View {
    
    var body: some View {
        Capsule()
            .fill(.white)
            .frame(height: 20)
            .overlay(
                Capsule()
                    .stroke(.gray, lineWidth: 2.0)
            )
            .overlay(
                Capsule()
                    .inset(by: 1)
                    .fill(.green)
                    .frame(width: 100)
            )
    }
}
4. 從上圖所看到控制了綠色條的長度,但我們需要把綠色長放到最左邊,另外我們也要取到整個控制條可以設置的長度,這時可以使用SwiftUI提供的一個控件GeometryReader,從GeometryReader元件可以取得的座標尺寸。
struct CapsuleProgressExample: View {
    
    var body: some View {
        Capsule()
            .fill(.white)
            .frame(height: 20)
            .overlay(
                Capsule()
                    .stroke(.gray, lineWidth: 2.0)
            )
            .overlay(
                GeometryReader { proxy in
                    Capsule()
                        .inset(by: 1)
                        .fill(.green)
                        .frame(width: proxy.size.width)
                }
            )
    }
}
從GeometryReader的size取後當前元件所佔的寬度和長度。
5. 知道從GeometryReader.size.width取得進度條可設的最大長度後,現在就可以增加一個百份比的變量percent設置其綠色條的佔比了,比如percent=0.5就是顯示一半的綠色條。由於我們以 overlay 將 GeometryReader 疊加上在原有的 Capsule 上,所以它的位置大小即是 原有Capsule 的空間。
struct CapsuleProgressExample: View {
    @State var percent = 0.5
    var body: some View {
        
        Capsule()
            .fill(.white)
            .frame(height: 20)
            .overlay(
                Capsule()
                    .stroke(.gray, lineWidth: 2.0)
            )
            .overlay(
                GeometryReader { proxy in
                    Capsule()
                        .inset(by: 1)
                        .fill(.green)
                        .frame(width: proxy.size.width * percent)
                }
            )
    }
}
6. 現在就可以用動畫把percent從0改到1,來演示整個進度條的效果。
struct CapsuleProgressExample: View {
    @State var percent = 0.0
    var body: some View {
        
        Capsule()
            .fill(.white)
            .frame(height: 20)
            .overlay(
                Capsule()
                    .stroke(.gray, lineWidth: 2.0)
            )
            .overlay(
                GeometryReader { proxy in
                    Capsule()
                        .inset(by: 1)
                        .fill(.green)
                        .frame(width: proxy.size.width * percent)
                        .animation(.linear(duration: 3.0), value: percent)
                }
            )
            .onTapGesture {
                percent = 1.0
            }
    }
}
7. 把上述代碼包成一個元件,就可以複用到其他地方。
struct CapsuleProgress: View {
    @State var percent = 0.0
    var body: some View {
        
        Capsule()
            .fill(.white)
            .frame(height: 20)
            .overlay(
                Capsule()
                    .stroke(.gray, lineWidth: 2.0)
            )
            .overlay(
                GeometryReader { proxy in
                    Capsule()
                        .inset(by: 1)
                        .fill(.green)
                        .frame(width: proxy.size.width * percent)
                        .animation(.linear(duration: 3.0), value: percent)
                }
            )
            .onTapGesture {
                percent = 1.0
            }
    }
}
struct CapsuleProgressExample: View {
    var body: some View {
        VStack {
            CapsuleProgress()
            CapsuleProgress()
        }
    }
}