在 SwiftUI 视图中两次引用相同路径的最佳方法是什么?
What is the best way to reference the same path twice in a SwiftUI View?
我正在尝试创建一个动态生成的按钮,它使用相同的路径绘制主要形状,然后绘制边框。完整代码如下。
我目前最好的方法是两次调用按钮生成器,但是因为它使用随机数据(设计约束),连续调用使用不同的数据,因此边框没有正确地与按钮形状重叠。见下图。
import SwiftUI
class ButtonPath {
func points(height: Int) ->
((Int,Int),(Int,Int),(Int,Int),
(Int,Int),(Int,Int),(Int,Int),
(Int,Int),(Int,Int),(Int,Int),
(Int,Int),(Int,Int),(Int,Int))
{
func rand() -> Int { Int.random(in: -5...5) }
func r2(x: Int) -> Int { Int.random(in: -x...x) }
let screen: CGRect = UIScreen.main.bounds
let widthMx = CGFloat(0.9)
let origin = (x:15, y:15)
let width = Int(screen.width * widthMx)
// Corner points
let tl = (x: origin.x + rand(), y: origin.x + rand()) // tl = Top Left, etc.
let tr = (x: origin.x + width + rand(), y: origin.y + rand())
let bl = (x: origin.x + rand(), y: origin.y + height + rand())
let br = (x: origin.x + width + rand(), y: origin.y + height + rand())
// Arc controls, we're drawing a rectangle counter-clockwise from the top left
let a1c1 = (x: origin.x + rand(), y: Int(Double(origin.y+height+rand()) * 0.3)) // a1c1 = Arc 1 Control 1
let a1c2 = (x: origin.x + rand(), y: Int(Double(origin.y+height+rand()) * 0.6))
let a2c1 = (x: Int(Double(origin.x+width+rand()) * 0.3), y: origin.y + height + rand())
let a2c2 = (x: Int(Double(origin.x+width+rand()) * 0.6), y: origin.y + height + rand())
let a3c1 = (x: origin.x + width + rand(), y: Int(Double(origin.y + height+rand()) * 0.6))
let a3c2 = (x: origin.x + width + rand(), y: Int(Double(origin.y + height+rand()) * 0.3))
let a4c1 = (x: Int(Double(origin.x+width+rand()) * 0.6), y: origin.y + rand())
let a4c2 = (x: Int(Double(origin.x+width+rand()) * 0.6), y: origin.y + rand())
return (
t1: tl, tr: tr, b1: bl, br: br,
a1c1: a1c1, a1c2: a1c2, a2c1: a2c1,
a2c2:a2c2, a3c1:a3c1, a3c2:a3c2, a4c1:a4c1, a4c2:a4c2
)
}
func path (height:Int) -> Path {
let (tl, tr, bl, br, a1c1, a1c2, a2c1, a2c2, a3c1, a3c2, a4c1, a4c2) = points(height: height)
let path1 = Path { path in
path.move( to: CGPoint(x: tl.0, y: tl.1) )
path.addCurve( to: CGPoint(x: bl.0, y: bl.1), control1: CGPoint(x: a1c1.0, y: a1c1.1), control2: CGPoint(x: a1c2.0, y: a1c2.1))
path.addCurve( to: CGPoint(x: br.0, y: br.1), control1: CGPoint(x: a2c1.0, y: a2c1.1), control2: CGPoint(x: a2c2.0, y: a2c2.1))
path.addCurve( to: CGPoint(x: tr.0, y: tr.1), control1: CGPoint(x: a3c1.0, y: a3c1.1), control2: CGPoint(x: a3c2.0, y: a3c2.1))
path.addCurve( to: CGPoint(x: tl.0, y: tl.1), control1: CGPoint(x: a4c1.0, y: a4c1.1), control2: CGPoint(x: a4c2.0, y: a4c2.1))
}
return path1
}
}
struct MyButton: View {
var color: String
var title: String
var height: Int
var path: (Path) {
get { ButtonPath().path(height: height) }
}
var body: some View {
VStack {
ZStack {
path.fill(Color(color))
path.stroke(Color.white, lineWidth: 4).shadow(color: Color("gray4"), radius: 4, x: -10, y: -10)
Text(self.title).font(.custom("Chaloops-Bold", size: 30)).tracking(5).shadow(radius: 2)
}
.foregroundColor(Color.white)
.frame(height:130)
}
}
}
struct Button_Previews: PreviewProvider {
static var previews: some View {
VStack {
MyButton(color: "red1", title: "SUBMIT", height: 100)
MyButton(color: "blue1", title: "SUBMIT", height: 100)
MyButton(color: "red1", title: "SUBMIT", height: 100)
MyButton(color: "blue1", title: "SUBMIT", height: 100)
}
}
}
这是如何完成的
变体 1:
struct MyButton: View {
var color: String
var title: String
var height: Int
var body: some View {
let path = ButtonPath().path(height: height)
return VStack {
ZStack {
path.fill(Color(color))
path.stroke(Color.white, lineWidth: 4).shadow(color: Color("gray4"), radius: 4, x: -10, y: -10)
Text(self.title).font(.custom("Chaloops-Bold", size: 30)).tracking(5).shadow(radius: 2)
}
.foregroundColor(Color.white)
.frame(height:130)
}
}
}
变体 2:
struct MyButton: View {
var color: String
var title: String
var height: Int
private let path: Path
init(color: String, title: String, height: Int) {
self.color = color
self.title = title
self.height = height
self.path = ButtonPath().path(height: height)
}
var body: some View {
VStack {
ZStack {
path.fill(Color(color))
path.stroke(Color.white, lineWidth: 4).shadow(color: Color("gray4"), radius: 4, x: -10, y: -10)
Text(self.title).font(.custom("Chaloops-Bold", size: 30)).tracking(5).shadow(radius: 2)
}
.foregroundColor(Color.white)
.frame(height:130)
}
}
}
我正在尝试创建一个动态生成的按钮,它使用相同的路径绘制主要形状,然后绘制边框。完整代码如下。
我目前最好的方法是两次调用按钮生成器,但是因为它使用随机数据(设计约束),连续调用使用不同的数据,因此边框没有正确地与按钮形状重叠。见下图。
import SwiftUI
class ButtonPath {
func points(height: Int) ->
((Int,Int),(Int,Int),(Int,Int),
(Int,Int),(Int,Int),(Int,Int),
(Int,Int),(Int,Int),(Int,Int),
(Int,Int),(Int,Int),(Int,Int))
{
func rand() -> Int { Int.random(in: -5...5) }
func r2(x: Int) -> Int { Int.random(in: -x...x) }
let screen: CGRect = UIScreen.main.bounds
let widthMx = CGFloat(0.9)
let origin = (x:15, y:15)
let width = Int(screen.width * widthMx)
// Corner points
let tl = (x: origin.x + rand(), y: origin.x + rand()) // tl = Top Left, etc.
let tr = (x: origin.x + width + rand(), y: origin.y + rand())
let bl = (x: origin.x + rand(), y: origin.y + height + rand())
let br = (x: origin.x + width + rand(), y: origin.y + height + rand())
// Arc controls, we're drawing a rectangle counter-clockwise from the top left
let a1c1 = (x: origin.x + rand(), y: Int(Double(origin.y+height+rand()) * 0.3)) // a1c1 = Arc 1 Control 1
let a1c2 = (x: origin.x + rand(), y: Int(Double(origin.y+height+rand()) * 0.6))
let a2c1 = (x: Int(Double(origin.x+width+rand()) * 0.3), y: origin.y + height + rand())
let a2c2 = (x: Int(Double(origin.x+width+rand()) * 0.6), y: origin.y + height + rand())
let a3c1 = (x: origin.x + width + rand(), y: Int(Double(origin.y + height+rand()) * 0.6))
let a3c2 = (x: origin.x + width + rand(), y: Int(Double(origin.y + height+rand()) * 0.3))
let a4c1 = (x: Int(Double(origin.x+width+rand()) * 0.6), y: origin.y + rand())
let a4c2 = (x: Int(Double(origin.x+width+rand()) * 0.6), y: origin.y + rand())
return (
t1: tl, tr: tr, b1: bl, br: br,
a1c1: a1c1, a1c2: a1c2, a2c1: a2c1,
a2c2:a2c2, a3c1:a3c1, a3c2:a3c2, a4c1:a4c1, a4c2:a4c2
)
}
func path (height:Int) -> Path {
let (tl, tr, bl, br, a1c1, a1c2, a2c1, a2c2, a3c1, a3c2, a4c1, a4c2) = points(height: height)
let path1 = Path { path in
path.move( to: CGPoint(x: tl.0, y: tl.1) )
path.addCurve( to: CGPoint(x: bl.0, y: bl.1), control1: CGPoint(x: a1c1.0, y: a1c1.1), control2: CGPoint(x: a1c2.0, y: a1c2.1))
path.addCurve( to: CGPoint(x: br.0, y: br.1), control1: CGPoint(x: a2c1.0, y: a2c1.1), control2: CGPoint(x: a2c2.0, y: a2c2.1))
path.addCurve( to: CGPoint(x: tr.0, y: tr.1), control1: CGPoint(x: a3c1.0, y: a3c1.1), control2: CGPoint(x: a3c2.0, y: a3c2.1))
path.addCurve( to: CGPoint(x: tl.0, y: tl.1), control1: CGPoint(x: a4c1.0, y: a4c1.1), control2: CGPoint(x: a4c2.0, y: a4c2.1))
}
return path1
}
}
struct MyButton: View {
var color: String
var title: String
var height: Int
var path: (Path) {
get { ButtonPath().path(height: height) }
}
var body: some View {
VStack {
ZStack {
path.fill(Color(color))
path.stroke(Color.white, lineWidth: 4).shadow(color: Color("gray4"), radius: 4, x: -10, y: -10)
Text(self.title).font(.custom("Chaloops-Bold", size: 30)).tracking(5).shadow(radius: 2)
}
.foregroundColor(Color.white)
.frame(height:130)
}
}
}
struct Button_Previews: PreviewProvider {
static var previews: some View {
VStack {
MyButton(color: "red1", title: "SUBMIT", height: 100)
MyButton(color: "blue1", title: "SUBMIT", height: 100)
MyButton(color: "red1", title: "SUBMIT", height: 100)
MyButton(color: "blue1", title: "SUBMIT", height: 100)
}
}
}
这是如何完成的
变体 1:
struct MyButton: View {
var color: String
var title: String
var height: Int
var body: some View {
let path = ButtonPath().path(height: height)
return VStack {
ZStack {
path.fill(Color(color))
path.stroke(Color.white, lineWidth: 4).shadow(color: Color("gray4"), radius: 4, x: -10, y: -10)
Text(self.title).font(.custom("Chaloops-Bold", size: 30)).tracking(5).shadow(radius: 2)
}
.foregroundColor(Color.white)
.frame(height:130)
}
}
}
变体 2:
struct MyButton: View {
var color: String
var title: String
var height: Int
private let path: Path
init(color: String, title: String, height: Int) {
self.color = color
self.title = title
self.height = height
self.path = ButtonPath().path(height: height)
}
var body: some View {
VStack {
ZStack {
path.fill(Color(color))
path.stroke(Color.white, lineWidth: 4).shadow(color: Color("gray4"), radius: 4, x: -10, y: -10)
Text(self.title).font(.custom("Chaloops-Bold", size: 30)).tracking(5).shadow(radius: 2)
}
.foregroundColor(Color.white)
.frame(height:130)
}
}
}