演示

學(xué)習(xí)內(nèi)容
- 創(chuàng)建自定義形狀
- 為自定義形狀添加漸變顏色
- 動畫自定義形狀
開始
啟動一個(gè)新的Xcode項(xiàng)目:
- 開啟Xcode
- 創(chuàng)建一個(gè)新的Xcode項(xiàng)目
- 選擇單視圖應(yīng)用程序,然后單擊下一步
- 為您的應(yīng)用命名(RingGraph),并確保用戶界面是Swift UI
- 最后,單擊“完成”
- 將ContentView文件名和結(jié)構(gòu)重命名為RingGraph,并確保在中將其引用重命名SceneDelegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let ringGraph = RingGraph()
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ringGraph)
self.window = window
window.makeKeyAndVisible()
}
}
您看到的所有ContentView引用RingGraph。
創(chuàng)建RingShape
創(chuàng)建一個(gè)名為Shapes的文件夾,并在其中創(chuàng)建一個(gè)名為的快速文件RingShape。
import SwiftUI
struct RingShape: Shape {
var percent: Double
var radius: CGFloat = 100
var animatableData: Double{
get{
return percent
}
set{
percent = newValue
}
}
func path(in rect: CGRect) -> Path {
let width = rect.width
let height = rect.height
let center = CGPoint(x: width / 2, y: height / 2)
let endAngle = Angle(degrees: ( percent / 100 * 360) - 90)
let radius = width / 2
return Path{ path in
path.addArc(center: center, radius: radius, startAngle: Angle(degrees: -90.0) , endAngle: endAngle, clockwise: false)
}
}
}
說明
- 要創(chuàng)建自定義形狀,該結(jié)構(gòu)必須符合Shape協(xié)議。
- 前兩個(gè)屬性是顯而易見的。至于第三個(gè)屬性,我們需要使用它來動畫化路徑的繪制。每次您需要為路徑設(shè)置動畫時(shí),請確保覆蓋該屬性并返回將更改要設(shè)置動畫的路徑狀態(tài)的屬性。
- 當(dāng)您遵守Shape協(xié)議時(shí),將需要覆蓋此方法path(in rect: CGRect) -> Path。
- 在path方法中,設(shè)置了用于繪制圓弧的常量。-90的值。因?yàn)槲蚁M麍D形從90度而不是0(默認(rèn)值)開始。
創(chuàng)建RingView
現(xiàn)在,該創(chuàng)建一個(gè)包含我們新創(chuàng)建RingShape的視圖了。
創(chuàng)建一個(gè)名為Views的新文件夾,并在其中創(chuàng)建一個(gè)名為RingView的swiftUI文件。
import SwiftUI
struct Ring: View {
@Binding var percent: Double
var thickness: CGFloat = 35
var fontSize:CGFloat = 15
var gradientColors = [Color.blue, Color.red]
var body: some View {
return drawRing()
}
private func drawRing() -> some View{
let formattedPercent = String(format: "%.f", CGFloat(self.percent))
return ZStack(alignment: .top) {
RingShape(percent: 100)
.stroke(style: StrokeStyle(lineWidth: self.thickness - 5))
.fill(Color.gray.opacity(0.2))
RingShape(percent: self.percent)
.stroke(style: StrokeStyle(lineWidth: self.thickness, lineCap: CGLineCap.round))
.fill(
LinearGradient(
gradient: .init(colors: gradientColors), startPoint: .init(x: 0.2, y: 0.4), endPoint: .init(x: 0.5, y: 1)
)
)
Text("\(formattedPercent)%")
.multilineTextAlignment(.trailing)
.font(.system(size: fontSize, weight: .black))
.offset(y: -thickness / 4)
.shadow(radius: 10)
}
}
}
struct Ring_Previews: PreviewProvider {
static var previews: some View {
Ring(percent: .constant(50))
}
}
這里要注意的一點(diǎn)是我如何創(chuàng)建RingPaths
第一個(gè)是帶有灰色的完整圓圈
第二個(gè)是將指示百分比水平的圓圈。
使用startPoint和endPoint來完成。
預(yù)覽效果

放在一起
創(chuàng)建一個(gè)名為Utils的新文件夾,并在其中創(chuàng)建一個(gè)名為Colors的快速文件。在該文件中添加以下代碼塊:
extension Color {
static var ring1color1: Color {
return Color("ring1color1")
}
static var ring1color2:Color {
return Color("ring1color2")
}
static var ring2color1:Color {
return Color("ring2color1")
}
static var ring2color2:Color {
return Color("ring2color2")
}
static var ring3color1:Color {
return Color("ring3color1")
}
static var ring3color2:Color {
return Color("ring3color2")
}
}
然后創(chuàng)建一個(gè)名為Modifiers的新文件夾,并添加一個(gè)NutrientModifier包含以下代碼塊的文件:
struct NutrientModifier: ViewModifier {
var color: Color = .red
func body(content: Content) -> some View {
content.foregroundColor(color)
.frame(width: 25, height: 25)
.cornerRadius(4)
}
}
這只是一個(gè)簡單的修飾符,我們將在短時(shí)間內(nèi)使用。
在RingGraph文件中,將它們添加到結(jié)構(gòu)的頂部:
@State var percent1: Double = 60
@State var percent2: Double = 70
@State var percent3: Double = 80
var gRing1:[Color] = [Color.ring1color1, Color.ring1color2]
var gRing2:[Color] = [Color.ring2color1, Color.ring2color2]
var gRing3:[Color] = [Color.ring3color1, Color.ring3color2]
private var thickness: CGFloat = 40
在body內(nèi),所有內(nèi)容并將此代碼放入其中
var body: some View {
return NavigationView {
VStack {
Text("今天你已經(jīng)消耗了 \(String(format: "%.1f", CGFloat((self.percent1 + self.percent2 + self.percent3) / 3)))%")
.font(.title)
.fontWeight(.bold)
.lineLimit(2)
.multilineTextAlignment(.center)
.padding(.horizontal, 30)
.frame(height: 70)
Text("保持好你的身體")
.multilineTextAlignment(.center)
.padding(.bottom, 30)
self.createGrapth().frame(minWidth: 0.0, maxWidth: .infinity)
Spacer()
HStack {
HStack{
Rectangle().modifier(NutrientModifier(color: .ring1color1) )
Text("碳水化合物")
}
Spacer()
HStack{
Rectangle().modifier(NutrientModifier(color: .ring2color2) )
Text("蛋白")
}
Spacer()
HStack{
Rectangle().modifier(NutrientModifier(color: .ring3color2) )
Text("脂肪")
}
}
}.padding().navigationBarTitle(Text("SwiftUI仿寫運(yùn)動"), displayMode: .inline).navigationBarItems(trailing: self.trailingButton())
}
}
這段代碼有錯誤,因?yàn)闆]有createGraph和trailingButton功能,現(xiàn)在創(chuàng)建這些:
將其放在RingGraph結(jié)構(gòu)內(nèi)但在body塊下面:
private func createGrapth() -> some View{
let width = UIScreen.main.bounds.width - 20
return
ZStack {
Ring(percent: self.$percent1, thickness: self.thickness, fontSize: 15, gradientColors: gRing1).frame(width: width - thickness, height: width - thickness )
Ring(percent: self.$percent2, thickness: self.thickness, fontSize: 15, gradientColors: gRing2).frame(width: width - thickness * 3, height: width - thickness * 3)
Ring(percent: self.$percent3, thickness: self.thickness, fontSize: 15, gradientColors: gRing3).frame(width: width - thickness * 5, height: width - thickness * 5)
}
}
private func trailingButton() -> some View{
return Button(action: {
withAnimation(.easeInOut(duration: 1)) {
self.percent1 = Double.random(in: 1...100)
self.percent2 = Double.random(in: 1...100)
self.percent3 = Double.random(in: 1...100)
}
}) {
Image(systemName: "arrow.clockwise")
.resizable()
.frame(width: 25, height: 30)
.foregroundColor(.ring3color2)
.aspectRatio(contentMode: ContentMode.fit)
}
}
如您所見,這些是2個(gè)獨(dú)立的函數(shù)。
第一個(gè)創(chuàng)建3個(gè)環(huán)
第二個(gè)創(chuàng)建為每個(gè)環(huán)生成1到100之間的3個(gè)隨機(jī)數(shù)。
- 代碼下載地址
點(diǎn)擊跳轉(zhuǎn)下載 - 歡迎點(diǎn)贊和反饋
本文為作者原著,歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明作者出處