Swift: 使 UIView 的轮廓像草图一样
Swift: Make the outline of a UIView sketch-like
我想让 UIView 的轮廓看起来像有人画的那样“波浪形”。
我有这个来自 PowerPoint 的例子,它允许这样做(应该适用于任何尺寸和角半径):
目前我拥有的是:
myView.layer.borderWidth = 10
myView.layer.borderColor = UIColor.blue.cgColor
myView.layer.cornerRadius = 5 // Optional
感谢
使用此扩展程序解决问题
导入基金会
导入 UIKit
extension UIView {
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.2
layer.shadowOffset = .zero
layer.shadowRadius = 5
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
@IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
@IBInspectable
var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
}
}
@IBInspectable
var borderColor: UIColor? {
get {
let color = UIColor.init(cgColor: layer.borderColor!) //UIColor.init(CGColor: layer.borderColor!)
return color
}
set {
layer.borderColor = newValue?.cgColor
}
}
@IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = newValue
}
}
@IBInspectable
var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
@IBInspectable
var shadowOffset: CGSize {
get {
return layer.shadowOffset
}
set {
layer.shadowOffset = newValue
}
}
@IBInspectable
var shadowColor: UIColor? {
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
return nil
}
set {
if let color = newValue {
layer.shadowColor = color.cgColor
} else {
layer.shadowColor = nil
}
}
}
}
看起来像这样
您可以使用 UIBezierPath
结合四边形曲线、直线、圆弧等来创建“波浪”线
我们将从一条简单的线开始,它是视图宽度的四分之一:
我们的路径包括:
- 移动到
0,0
- 将行添加到
80,0
如果我们将其更改为四曲线:
现在我们正在做:
- 移动到
0,0
- 使用控制点
40,40
将四边形曲线添加到 80,0
如果我们添加另一个方向相反的四边形曲线:
现在我们正在做:
- 移动到
0,0
- 使用控制点
40,40
将四边形曲线添加到 80,0
- 使用控制点
120,-40
将四边形曲线添加到 160,0
我们可以扩展视图的宽度:
当然,这看起来不像您的“草图”目标,所以让我们将控制点偏移量从 40 更改为 2:
现在看起来更像是手绘的“素描”线。
它太统一了,但是,它部分超出了视图的范围,所以让我们将它插入 8 磅,而不是四个 25% 的片段,我们将使用(例如)其中的五个片段宽度:
0.15, 0.2, 0.2, 0.27, 0.18
如果我们采用相同的方法从右侧向下,返回底部,然后从左侧向上,我们可以得到:
下面是生成该视图的一些示例代码:
class SketchBorderView: UIView {
let borderLayer: CAShapeLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.strokeColor = UIColor.blue.cgColor
layer.addSublayer(borderLayer)
backgroundColor = .yellow
}
override func layoutSubviews() {
let incrementVals: [CGFloat] = [
0.15, 0.2, 0.2, 0.27, 0.18,
]
let lineOffsets: [[CGFloat]] = [
[ 1.0, -2.0],
[-1.0, 2.0],
[-1.0, -2.0],
[ 1.0, 2.0],
[ 0.0, -2.0],
]
let pth: UIBezierPath = UIBezierPath()
// inset bounds by 8-pts so we can draw the "wavy border"
// inside our bounds
let r: CGRect = bounds.insetBy(dx: 8.0, dy: 8.0)
var ptDest: CGPoint = .zero
var ptControl: CGPoint = .zero
// start at top-left
ptDest = r.origin
pth.move(to: ptDest)
// we're at top-left
for i in 0..<incrementVals.count {
ptDest.x += r.width * incrementVals[i]
ptDest.y = r.minY + lineOffsets[i][0]
ptControl.x = pth.currentPoint.x + ((ptDest.x - pth.currentPoint.x) * 0.5)
ptControl.y = r.minY + lineOffsets[i][1]
pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
}
// now we're at top-right
for i in 0..<incrementVals.count {
ptDest.y += r.height * incrementVals[i]
ptDest.x = r.maxX + lineOffsets[i][0]
ptControl.y = pth.currentPoint.y + ((ptDest.y - pth.currentPoint.y) * 0.5)
ptControl.x = r.maxX + lineOffsets[i][1]
pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
}
// now we're at bottom-right
for i in 0..<incrementVals.count {
ptDest.x -= r.width * incrementVals[i]
ptDest.y = r.maxY + lineOffsets[i][0]
ptControl.x = pth.currentPoint.x - ((pth.currentPoint.x - ptDest.x) * 0.5)
ptControl.y = r.maxY + lineOffsets[i][1]
pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
}
// now we're at bottom-left
for i in 0..<incrementVals.count {
ptDest.y -= r.height * incrementVals[i]
ptDest.x = r.minX + lineOffsets[i][0]
ptControl.y = pth.currentPoint.y - ((pth.currentPoint.y - ptDest.y) * 0.5)
ptControl.x = r.minX + lineOffsets[i][1]
pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
}
borderLayer.path = pth.cgPath
}
}
和一个示例控制器:
class SketchTestVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let v = SketchBorderView()
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
v.centerXAnchor.constraint(equalTo: g.centerXAnchor),
v.centerYAnchor.constraint(equalTo: g.centerYAnchor),
v.widthAnchor.constraint(equalToConstant: 320.0),
v.heightAnchor.constraint(equalTo: v.widthAnchor),
])
}
}
虽然使用该代码,我们仍然有太多的统一性,因此在实际使用中我们希望随机化段数、段的宽度和控制点偏移。
当然,要获得“圆角矩形”,您需要在角处添加圆弧。
我希望这能让你顺利上路。
我想让 UIView 的轮廓看起来像有人画的那样“波浪形”。
我有这个来自 PowerPoint 的例子,它允许这样做(应该适用于任何尺寸和角半径):
目前我拥有的是:
myView.layer.borderWidth = 10
myView.layer.borderColor = UIColor.blue.cgColor
myView.layer.cornerRadius = 5 // Optional
感谢
使用此扩展程序解决问题
导入基金会 导入 UIKit
extension UIView {
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.2
layer.shadowOffset = .zero
layer.shadowRadius = 5
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
@IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
@IBInspectable
var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
}
}
@IBInspectable
var borderColor: UIColor? {
get {
let color = UIColor.init(cgColor: layer.borderColor!) //UIColor.init(CGColor: layer.borderColor!)
return color
}
set {
layer.borderColor = newValue?.cgColor
}
}
@IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = newValue
}
}
@IBInspectable
var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
@IBInspectable
var shadowOffset: CGSize {
get {
return layer.shadowOffset
}
set {
layer.shadowOffset = newValue
}
}
@IBInspectable
var shadowColor: UIColor? {
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
return nil
}
set {
if let color = newValue {
layer.shadowColor = color.cgColor
} else {
layer.shadowColor = nil
}
}
}
}
看起来像这样
您可以使用 UIBezierPath
结合四边形曲线、直线、圆弧等来创建“波浪”线
我们将从一条简单的线开始,它是视图宽度的四分之一:
我们的路径包括:
- 移动到
0,0
- 将行添加到
80,0
如果我们将其更改为四曲线:
现在我们正在做:
- 移动到
0,0
- 使用控制点
40,40
将四边形曲线添加到
80,0
如果我们添加另一个方向相反的四边形曲线:
现在我们正在做:
- 移动到
0,0
- 使用控制点
40,40
将四边形曲线添加到 - 使用控制点
120,-40
将四边形曲线添加到
80,0
160,0
我们可以扩展视图的宽度:
当然,这看起来不像您的“草图”目标,所以让我们将控制点偏移量从 40 更改为 2:
现在看起来更像是手绘的“素描”线。
它太统一了,但是,它部分超出了视图的范围,所以让我们将它插入 8 磅,而不是四个 25% 的片段,我们将使用(例如)其中的五个片段宽度:
0.15, 0.2, 0.2, 0.27, 0.18
如果我们采用相同的方法从右侧向下,返回底部,然后从左侧向上,我们可以得到:
下面是生成该视图的一些示例代码:
class SketchBorderView: UIView {
let borderLayer: CAShapeLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.strokeColor = UIColor.blue.cgColor
layer.addSublayer(borderLayer)
backgroundColor = .yellow
}
override func layoutSubviews() {
let incrementVals: [CGFloat] = [
0.15, 0.2, 0.2, 0.27, 0.18,
]
let lineOffsets: [[CGFloat]] = [
[ 1.0, -2.0],
[-1.0, 2.0],
[-1.0, -2.0],
[ 1.0, 2.0],
[ 0.0, -2.0],
]
let pth: UIBezierPath = UIBezierPath()
// inset bounds by 8-pts so we can draw the "wavy border"
// inside our bounds
let r: CGRect = bounds.insetBy(dx: 8.0, dy: 8.0)
var ptDest: CGPoint = .zero
var ptControl: CGPoint = .zero
// start at top-left
ptDest = r.origin
pth.move(to: ptDest)
// we're at top-left
for i in 0..<incrementVals.count {
ptDest.x += r.width * incrementVals[i]
ptDest.y = r.minY + lineOffsets[i][0]
ptControl.x = pth.currentPoint.x + ((ptDest.x - pth.currentPoint.x) * 0.5)
ptControl.y = r.minY + lineOffsets[i][1]
pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
}
// now we're at top-right
for i in 0..<incrementVals.count {
ptDest.y += r.height * incrementVals[i]
ptDest.x = r.maxX + lineOffsets[i][0]
ptControl.y = pth.currentPoint.y + ((ptDest.y - pth.currentPoint.y) * 0.5)
ptControl.x = r.maxX + lineOffsets[i][1]
pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
}
// now we're at bottom-right
for i in 0..<incrementVals.count {
ptDest.x -= r.width * incrementVals[i]
ptDest.y = r.maxY + lineOffsets[i][0]
ptControl.x = pth.currentPoint.x - ((pth.currentPoint.x - ptDest.x) * 0.5)
ptControl.y = r.maxY + lineOffsets[i][1]
pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
}
// now we're at bottom-left
for i in 0..<incrementVals.count {
ptDest.y -= r.height * incrementVals[i]
ptDest.x = r.minX + lineOffsets[i][0]
ptControl.y = pth.currentPoint.y - ((pth.currentPoint.y - ptDest.y) * 0.5)
ptControl.x = r.minX + lineOffsets[i][1]
pth.addQuadCurve(to: ptDest, controlPoint: ptControl)
}
borderLayer.path = pth.cgPath
}
}
和一个示例控制器:
class SketchTestVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let v = SketchBorderView()
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
v.centerXAnchor.constraint(equalTo: g.centerXAnchor),
v.centerYAnchor.constraint(equalTo: g.centerYAnchor),
v.widthAnchor.constraint(equalToConstant: 320.0),
v.heightAnchor.constraint(equalTo: v.widthAnchor),
])
}
}
虽然使用该代码,我们仍然有太多的统一性,因此在实际使用中我们希望随机化段数、段的宽度和控制点偏移。
当然,要获得“圆角矩形”,您需要在角处添加圆弧。
我希望这能让你顺利上路。