我可以使用具有
UIColor(patternImage: UIImage(named: "pattern.png"))
的图像实现交替条纹图案填充。
但是,如何使用一些简单紧凑的代码以编程方式实现交替条纹图案填充?
这是我想要实现的两个示例。
问题
使用代码,如何使用 Swift 用不同的交替颜色条纹填充
?UIView
(1) 从上到下的两种颜色交替图案 (90 度)?
(2) 从左上到右下的三色交替图案 (45度)?
对于我的应用程序来说,创建可用作背景颜色的图案
UIColor
更有帮助且性能更高。
一旦我将其添加为
UIColor
的扩展,我可以轻松地将其放在代码中的任何位置,即使视图旋转、缩小和增长,它也将始终完美填充!
你这样使用它:
view.backgroundColor = UIColor.red.patternStripes()
view.backgroundColor = UIColor.red.patternStripes(color2: .darkGray)
view.backgroundColor = UIColor.red.patternStripes(color2: .darkGray, barThickness: 25.0)
并获得这种美丽:
这是 UIColor 的扩展:
extension UIColor {
/// make a diagonal striped pattern
func patternStripes(color2: UIColor = .white, barThickness t: CGFloat = 25.0) -> UIColor {
let dim: CGFloat = t * 2.0 * sqrt(2.0)
let img = UIGraphicsImageRenderer(size: .init(width: dim, height: dim)).image { context in
// rotate the context and shift up
context.cgContext.rotate(by: CGFloat.pi / 4.0)
context.cgContext.translateBy(x: 0.0, y: -2.0 * t)
let bars: [(UIColor,UIBezierPath)] = [
(self, UIBezierPath(rect: .init(x: 0.0, y: 0.0, width: dim * 2.0, height: t))),
(color2,UIBezierPath(rect: .init(x: 0.0, y: t, width: dim * 2.0, height: t)))
]
bars.forEach { $0.0.setFill(); $0.1.fill() }
// move down and paint again
context.cgContext.translateBy(x: 0.0, y: 2.0 * t)
bars.forEach { $0.0.setFill(); $0.1.fill() }
}
return UIColor(patternImage: img)
}
}
基本上,我正在创建一个临时绘图空间,供一个小矩形进行绘制。我用一种图案来绘制它,重复时该图案将在所有侧面无缝匹配。然后我拍了一张照片并告诉
UIColor
将其用作图案。
诀窍是,你必须使图案图像足够大,以便将每个条形绘制两次。结果是条形厚度 * 2 * sqrt(2)。
我知道我可能会因为风格而受到一些打击。这是我的想法:
t
这样的单字母变量,这不太具有描述性。我的辩护:我认为这使方程更具可读性,并且当它仅限于接下来的几行时感觉还可以。4.0
而不仅仅是4
)。我宁愿不使用小数,但我发现使用小数的编译时间更好。在更大的项目中,它可以产生巨大的影响。我也承认它读起来可能不太漂亮,但对类型的明确性甚至可以对人类有所帮助。3 种颜色: 我刚刚意识到原来的问题是要求 3 种颜色,所以这里有一个版本......
首先,数学:
更新代码:
extension UIColor {
/// make a diagonal striped pattern
func pattern3Stripes(color2: UIColor, color3: UIColor, barThickness t: CGFloat = 25.0) -> UIColor {
let sqrt2: CGFloat = sqrt(2.0)
let dim: CGFloat = t * 3.0 * sqrt2
let size: CGSize = .init(width: dim, height: dim)
let img = UIGraphicsImageRenderer(size: size).image { context in
// rotate the context and shift up
context.cgContext.rotate(by: CGFloat.pi / 4.0)
context.cgContext.translateBy(x: 0.0, y: -3.0 * t)
let bars: [(UIColor,UIBezierPath)] = [
(self, UIBezierPath(rect: .init(x: 0.0, y: 0.0, width: dim * sqrt2, height: t))),
(color2,UIBezierPath(rect: .init(x: 0.0, y: t, width: dim * sqrt2, height: t))),
(color3,UIBezierPath(rect: .init(x: 0.0, y: 2.0 * t, width: dim * sqrt2, height: t)))
]
bars.forEach { $0.0.setFill(); $0.1.fill() }
// move down and paint again
context.cgContext.translateBy(x: 0.0, y: 3.0 * t)
bars.forEach { $0.0.setFill(); $0.1.fill() }
}
return UIColor(patternImage: img)
}
}
用途:
view.backgroundColor = UIColor.green.pattern3Stripes(color2: .white, color3: .red, barThickness: 25.0)
结果:
可以使用以下代码实现使用具有两个、三个或更多颜色条纹且可调节条纹宽度和旋转的条纹图案填充视图。此代码提供了三色条纹图案,如下面的示例图片所示。
步骤:
- 将以下代码添加到 ViewController.swift
- 将 UIView 添加到 Storyboard。
- 为 Storyboard 上的 UIView 添加新的对齐约束;容器中水平 = 0,容器中垂直 = 0。
- 在 Identity Inspector 中,将 UIView 自定义类设置为“colorStripesView”。
- 将Storyboard上的UIView连接到代码中的viewPattern IBOutlet。
代码:
//// ViewController.swift
//// 1. Add the below code to ViewController.swift
//// 2. Add a UIView to Storyboard.
//// 3. Add new alignment contstraints to the UIView on the Storyboard; Horizontally in Container = 0, Vertically in Container = 0.
//// 4. In the Identity Inspector, set the UIView custom class to 'colorStripesView'.
//// 5. Connect the UIView on the Storyboard to the viewPattern IBOutlet in the code.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var viewPattern: UIView!
override func viewDidLoad() {
super.viewDidLoad()
//// Extend width and height constraints by factor of 2 for viewPattern rotation.
let widthConstraint = NSLayoutConstraint(item: viewPattern, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: (max(view.bounds.height, view.bounds.width)*2))
viewPattern.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: viewPattern, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: (max(view.bounds.height, view.bounds.width)*2))
viewPattern.addConstraint(heightConstraint)
//// Rotate pattern 0 degrees - vertical.
//viewPattern.transform = CGAffineTransformMakeRotation(CGFloat(M_PI*0/180))
//// Rotate pattern 45 degrees - diagonal top right to bottom left.
//viewPattern.transform = CGAffineTransformMakeRotation(CGFloat(M_PI*45/180))
//// Rotate pattern 90 degrees - horizontal.
//viewPattern.transform = CGAffineTransformMakeRotation(CGFloat(M_PI*90/180))
//// Rotate pattern 135 degrees - diagonal top left to bottom right.
viewPattern.transform = CGAffineTransformMakeRotation(CGFloat(M_PI*135/180))
//// Set view color
viewPattern.backgroundColor = UIColor.clearColor()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
class colorStripesView: UIView {
override func drawRect(rect: CGRect) {
//// Set pattern tile colors width and height; adjust the color width to adjust pattern.
let color1 = UIColor(red: 255/255, green: 255/255, blue: 10/255, alpha: 1.0)
let color1Width: CGFloat = 10
let color1Height: CGFloat = 10
let color2 = UIColor(red: 0/255, green: 0/255, blue: 254/255, alpha: 1.0)
let color2Width: CGFloat = 10
let color2Height: CGFloat = 10
let color3 = UIColor(red: 0/255, green: 128/255, blue: 128/255, alpha: 1.0)
let color3Width: CGFloat = 10
let color3Height: CGFloat = 10
//// Set pattern tile orientation vertical.
let patternWidth: CGFloat = (color1Width + color2Width + color3Width)
let patternHeight: CGFloat = min(color1Height, color2Height, color3Height)
//// Set pattern tile size.
let patternSize = CGSize(width: patternWidth, height: patternHeight)
//// Draw pattern tile
let context = UIGraphicsGetCurrentContext()
UIGraphicsBeginImageContextWithOptions(patternSize, false, 0.0)
let color1Path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: color1Width, height: color1Height))
color1.setFill()
color1Path.fill()
let color2Path = UIBezierPath(rect: CGRect(x: color1Width, y: 0, width: color2Width, height: color2Height))
color2.setFill()
color2Path.fill()
let color3Path = UIBezierPath(rect: CGRect(x: color1Width + color2Width, y: 0, width: color3Width, height: color3Height))
color3.setFill()
color3Path.fill()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//// Draw pattern in view
UIColor(patternImage: image).setFill()
CGContextFillRect(context, rect)
}
}
模拟器:
当然可以。
您可以使用 Core Image 过滤器绘制条纹,或者您可以创建 UIImage 的自定义子类并实现
drawRect
方法。
CIFilter
:我在 Github 上有一个名为 CIFilterTest(链接)的示例项目,可让您尝试所有不同类型的核心图像过滤器。它向系统询问可用过滤器的列表,然后尝试构建一个 UI 以输入每个过滤器的不同设置。它不支持一些更特殊用途的输入,但它可以让您尝试
CIStripesGenerator
,这是将为您创建交替条纹的过滤器。该程序是用 Objective-C 编写的,但它应该可以让您了解总体思路。请注意,看起来 CIStripesGenerator
滤镜只会生成 2 种交替颜色的条纹,因此您无法将其用于 3 色案例。
drawRect
:在
drawRect
中,您将创建一系列 UIBezierPaths 并用交替颜色填充它们。对于垂直条纹,您可以使用简单的 bezierPathWithRect:
init 方法。对于对角线,您需要使用 moveToPoint
和 lineToPoint
创建水平条纹,或者对矩形路径应用旋转变换。
这是一个在其内部绘制 2 个彩色矩形的示例类:
class StripedView: UIView {
override func drawRect(rect: CGRect) {
//Create a rect for the lefthand stripe
var rect = CGRectInset(bounds, 10, 10)
rect.size.width /= 2
var bezier = UIBezierPath(rect: rect) //Create a bezier for the left stripe
//Set the fill color to blue for the first rect
UIColor.blueColor().setFill()
bezier.fill()
//Shift the rect over for the righthand stripe
rect.origin.x = rect.size.width
bezier = UIBezierPath(rect: rect) //Create a bezier for the right stripe
UIColor.redColor().setFill()
bezier.fill()
}
}
您需要扩展上面的代码以创建一系列重复条纹而不是 2 个矩形,并使用 3 种而不是 2 种颜色。
将条纹旋转 45 度更为复杂。最简单的方法可能是通过沿视图边界绘制每个条纹的角来创建对角条纹,然后使用
UIBezierPath
和 moveToPoint
调用为每个条纹创建 lineToPoint
。这超出了论坛帖子的范围。
class Ruled: UIView {
override func draw(_ rect: CGRect) {
let T: CGFloat = 15 // desired thickness of lines
let G: CGFloat = 30 // desired gap between lines
let W = rect.size.width
let H = rect.size.height
guard let c = UIGraphicsGetCurrentContext() else { return }
c.setStrokeColor(UIColor.orange.cgColor)
c.setLineWidth(T)
var p = -(W > H ? W : H) - T
while p <= W {
c.move( to: CGPoint(x: p-T, y: -T) )
c.addLine( to: CGPoint(x: p+T+H, y: T+H) )
c.strokePath()
p += G + T + T
}
}
}