如何使用 Swift 以编程方式使用交替条纹图案填充 UIView?

Posted

技术标签:

【中文标题】如何使用 Swift 以编程方式使用交替条纹图案填充 UIView?【英文标题】:How to fill a UIView with an alternating stripe pattern programmatically using Swift? 【发布时间】:2016-08-27 14:08:36 【问题描述】:

我可以使用带有UIColor(patternImage: UIImage(named: "pattern.png")) 的图像实现交替的条纹图案填充。

但是,如何使用一些简单的紧凑代码以编程方式实现交替条纹图案填充绘制?

这是我想要实现的两个示例。

问题

使用代码,如何使用 Swift 用不同的交替颜色条纹填充 UIView

(1) 一种从上到下的两色交替图案 (90 度)?

(2) 从左上到右下的三色交替图案 (45度)?

【问题讨论】:

投反对票?我问了一个明确的问题。 更多的反对票在 cmets 中没有解释为什么?哦,互联网的乐趣。有一个伟大的一天神秘访客:-) 任何对此投反对票的人都非常奇怪——这一定是个意外。 欣赏它,@Fattie! 顺便说一句,另一个很好的答案:***.com/a/35820837/294884 【参考方案1】:

可以使用以下代码来填充具有两个、三个或更多颜色条纹的条纹图案的视图,这些条纹的宽度和旋转可调节。这段代码给出了一个三色条纹图案,如下图示例所示。

步骤:

    将以下代码添加到 ViewController.swift 中 向 Storyboard 添加 UIView。 为 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)
        
    

模拟器:

【讨论】:

【参考方案2】:

对于我的应用程序,创建可用作背景颜色的图案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 这样的一个字母变量,它的描述性不是很好。我的辩护:我认为它使方程式更具可读性,并且当它仅限于接下来的几行时感觉还可以。 元组。与 #1 相同 使用小数(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)

结果:

【讨论】:

我认为我们不需要负零,常规零就足够了。 谢谢@ACLima,我同意。我不知道我为什么这样做……最初可能有一些其他的价值。【参考方案3】:

当然可以。

您可以使用 Core Image 过滤器绘制条纹,或者您可以创建 UIImage 的自定义子类并实现 drawRect 方法。

编辑

使用CIFilter:

我在 Github 上有一个名为 CIFilterTest(链接)的示例项目,可让您尝试所有不同类型的 Core Image Filters。它询问系统以获取可用过滤器的列表,然后尝试构建一个 UI 以输入每个过滤器的不同设置。它不支持一些更特殊用途的输入,但它可以让您试用CIStripesGenerator,它是为您创建交替条纹的过滤器。该程序是用 Objective-C 编写的,但它应该给你一个大致的想法。请注意,CIStripesGenerator 过滤器似乎只会生成 2 种交替颜色的条纹,因此您无法将它用于您的 3 色情况。

使用drawRect:

drawRect 中,您将创建一系列 UIBezierPaths 并用交替的颜色填充它们。对于垂直条纹,您可以使用简单的bezierPathWithRect: init 方法。对于对角线,您需要使用moveToPointlineToPoint 创建水平条纹,或者对矩形路径应用旋转变换。

编辑#2:

这是一个在自身内部绘制 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 度更复杂。可能最简单的方法是通过沿视图边界绘制出每个条纹的角来创建对角条纹,然后使用moveToPointlineToPoint 调用为每个条纹创建UIBezierPath。这超出了论坛帖子的范围。

【讨论】:

谢谢,但不是这样。这只会在视图中创建两条粗线,而不是条纹图案。 是的,我意识到这一点。我给你的代码展示了如何通过实现 drawRect 方法在 UIView 的自定义子类中绘制多种颜色的矩形块。您应该能够从该代码开始并实现您所需要的。不过,看起来有人为你工作。 我做了自己的原创工作,并尝试为 *** 编写一个模型答案,以便任何人都可以在自己的项目中复制它。它并不完美,但它是建设性的,并且结果会在过渡期间运作良好。

以上是关于如何使用 Swift 以编程方式使用交替条纹图案填充 UIView?的主要内容,如果未能解决你的问题,请参考以下文章

检测图像中的莫尔条纹 - iOS Swift

R语言使用ggplot2可视化:使用ggpattern包在分组条形图中添加自定义条纹图案添加阴影条纹或其他图案或纹理(add hatches, stripes or another pattern

如何使用 Swift 以编程方式创建 UILabel?

如何使用 Swift 以编程方式添加约束

如何使用 Swift 以编程方式分配 IBOutlet?

如何使用 Swift 以编程方式创建 Hexagon UIButton 或 Clickable ImageView