寫APP那件事#9.客製 App 畫面

Daisy
彼得潘的 Swift iOS / Flutter App 開發教室
11 min readAug 16, 2020

溫故知新篇

用程式寫出的起始畫面,而不是用storyboard拉出

*程式畫圖步驟1234

1.利用 UIBezierPath 繪製路徑(B)

繪製前,需先製作一個view-畫板(A)

製作畫板let view = UIView()常用路徑寫法如下:1.線條: let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 10, y: 10))
path.close()
2.圓形:let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 10, height: 10))3.長方形: let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 10, height: 10))4.圓角長方形: let path=UIBezierPath.init(roundedRect:CGRect(x: 0, y: 0, width: 10, height: 10), cornerRadius: 5)From:Peter的補充
若只想特定方位有圓角,利用 UIBezierPath 的init(roundedRect:byRoundingCorners:cornerRadii:)
byRoundingCorners: 型別為 UIRectCorner,控制長方形圓角的位置。
cornerRadii: 控制圓角的程度,在此我們傳入的 CGSize 只要指定寬度即可,高度可設為 0。
【寫法】
let path = UIBezierPath(roundedRect: CGRect(x: 10, y: 10, width: 80, height: 50), byRoundingCorners: [.bottomLeft, .bottomRight], cornerRadii: CGSize(width: 5, height: 0))
5.在圓上的曲線:
let aDegree = CGFloat.pi / 180
let path = UIBezierPath(arcCenter: CGPoint(x: 0, y: 0), radius: 10, startAngle: aDegree * 0, endAngle: aDegree * 180, clockwise: true)
6-1.特定弧度的曲線,1個控制點:
path.addQuadCurve(to: CGPoint(x: 10, y: 10), controlPoint: CGPoint(x: 50, y: 50))
6-2.特定弧度的曲線,2個控制點:
path.addCurve(to: CGPoint(x: 10, y: 10), controlPoint1: CGPoint(x: 50, y: 50), controlPoint2: CGPoint(x: 80, y: 80))

參考1.運用 UIBezierPath 繪製各種形狀

2.利用 CAShapeLayer製作圖層(C)

3.最後把 CAShapeLayer 加到 view 上(A)

讓路徑-線條( B )/圖層-紙張( C ),都能覆蓋於畫板( A )上

let circleLayer = CAShapeLayer()
circleLayer.path = path.cgPath
應用1-填滿,內建為黑色
view.layer.addSublayer(circleLayer)
應用2-只有線條,需設定顏色/寬度
circleLayer.strokeColor = CGColor(Red: 10, green: 10, blue: 10, alpha: 1)
circleLayer.lineWidth = 10
view.layer.addSublayer(circleLayer)

4.進階調整- 讓View縮放/位移/旋轉/鏡像翻轉

1.縮放5倍
寫法1-View.transform = CGAffineTransform(scaleX: 5, y: 5)
寫法2-View.transform = CGAffineTransform.identity.scaledBy(x: 5, y: 5)
2.位移
View.transform = CGAffineTransform(translationX: 10, y: 50)
3.旋轉
let oneDegree = CGFloat.pi / 180
View.transform = CGAffineTransform(rotationAngle: oneDegree * 45)
4.鏡像翻轉
View.transform = CGAffineTransform(scaleX: -1, y: 1)
From:Peter的提醒
結合縮放,位移和旋轉值得注意的,結合縮放,位移和旋轉時,順序很重要,因為背後牽扯到矩陣的數學運算,所以先 translatedBy 再 scaledBy 跟先 scaledBy 再 translatedBy 是不一樣的,通常先 translatedBy 再 scaledBy 會是我們想要的效果。
【寫法】
let oneDegree = CGFloat.pi / 180
View.transform = CGAffineTransform.identity.translatedBy(x: 100, y: 300).scaledBy(x: 0.5, y: 0.5).rotated(by: oneDegree * 45)

參考2.

*動畫(加在layer上)

動畫[到達終點]
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 5
.....Layer.add(animation, forKey: nil)
重覆的動畫
animation.repeatCount = .greatestFiniteMagnitude
清除線條的動畫效果[回到起點]
let animation = CABasicAnimation(keyPath: "strokeStart")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 5
.....Layer.add(animation, forKey: nil)

參考3.

*加入文字(使用mask)

本來想做個漸層文字,但有漸層,文字就跑掉,文字置中下方,就沒漸層

漸層與置中,不可兼得

import SwiftUI
struct DrawView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
let view = UIView()
//circle
let circlepath = UIBezierPath(ovalIn: CGRect(x: 30, y: 160, width: 320, height: 320))

let circleLayer = CAShapeLayer()
circleLayer.path = circlepath.cgPath
circleLayer.fillColor=UIColor.systemPink.cgColor

circleLayer.strokeColor = CGColor(red: 220/255, green:100/255, blue: 100/255, alpha: 255)
circleLayer.lineWidth = 15
view.layer.addSublayer(circleLayer)

//love1
let love1view=UIView()
let love1path=UIBezierPath.init(roundedRect:CGRect(x: 340, y: -30, width: 100, height: 200),byRoundingCorners:[.topLeft,.topRight] ,cornerRadii: CGSize(width: 50, height: 0))
let love1Layer=CAShapeLayer()
love1Layer.path=love1path.cgPath
love1Layer.fillColor=UIColor.white.cgColor

love1view.layer.addSublayer(love1Layer)
let oneDegree = CGFloat.pi / 180
love1view.transform = CGAffineTransform(rotationAngle: oneDegree * 45)

//love2
let love2view=UIView()

let love2path=UIBezierPath.init(roundedRect:CGRect(x: -170, y: 240, width: 100, height: 200),byRoundingCorners:[.topLeft,.topRight] ,cornerRadii: CGSize(width: 50, height: 0))

let love2Layer=CAShapeLayer()
love2Layer.path=love2path.cgPath
love2Layer.fillColor=UIColor.white.cgColor
love2view.layer.addSublayer(love2Layer)
love2view.transform = CGAffineTransform(rotationAngle: oneDegree * -45)
view.addSubview(love1view)
view.addSubview(love2view)

//animation
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 5
animation.repeatCount = .greatestFiniteMagnitude
circleLayer.add(animation, forKey: nil)

//label
let lable=UILabel()
lable.text="iTalk"
lable.font=UIFont.systemFont(ofSize: 100)
lable.sizeToFit()
let lableview=UIView(frame:CGRect(x: 100, y: 500, width: 500, height: 500))
let gradientLayer=CAGradientLayer()
gradientLayer.frame=lableview.bounds
gradientLayer.colors=[UIColor.systemBlue.cgColor,UIColor.systemRed.cgColor]
lableview.layer.addSublayer(gradientLayer)
lableview.mask=lable

view.addSubview(lableview)

return view

}

func updateUIView(_ uiView: UIView, context: Context) {
}

}
struct ContentView: View {
var body: some View {
DrawView()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

--

--