【问题标题】:Draw dotted (not dashed!) line, with IBDesignable in 2017在 2017 年使用 IBDesignable 绘制虚线(不是虚线!)
【发布时间】:2014-11-19 00:42:28
【问题描述】:

用 UIKit 很容易画出虚线。所以:

CGFloat dashes[] = {4, 2};
[path setLineDash:dashes count:2 phase:0];
[path stroke];

有什么方法可以画出真正的虚线吗?

【问题讨论】:

  • 这是绘制对角线的绝妙方法! :) stackoverflow.com/a/45228178/294884
  • 感谢 Fattie,我稍加修改就可以根据视图高度计算点数,让 totalDynamicDots = bounds.size.height / CGFloat(3);让 itemLength = fullHeight / totalDynamicDots
  • 我们可以有一个水平的版本吗? @Fattie

标签: ios swift uiview uikit


【解决方案1】:

将线帽样式设置为圆形,并将“on”长度设置为一个很小的数字。

Swift 游乐场示例:

import UIKit
import PlaygroundSupport

let path = UIBezierPath()
path.move(to: CGPoint(x:10,y:10))
path.addLine(to: CGPoint(x:290,y:10))
path.lineWidth = 8

let dashes: [CGFloat] = [0.001, path.lineWidth * 2]
path.setLineDash(dashes, count: dashes.count, phase: 0)
path.lineCapStyle = CGLineCap.round

UIGraphicsBeginImageContextWithOptions(CGSize(width:300, height:20), false, 2)

UIColor.white.setFill()
UIGraphicsGetCurrentContext()!.fill(.infinite)

UIColor.black.setStroke()
path.stroke()

let image = UIGraphicsGetImageFromCurrentImageContext()
let view = UIImageView(image: image)
PlaygroundPage.current.liveView = view

UIGraphicsEndImageContext()

结果:


对于objective-C,使用与问题中相同的示例类,只需添加

CGContextSetLineCap(cx, kCGLineCapRound);

在调用 CGContextStrokePath 之前,更改 ra 数组值以匹配我的 Swift 代码。

【讨论】:

  • 关键信息在我回答的第一行(英文文本)。剩下的就是肉汁了。
  • 我发现将 on 长度设置为 0.01 会给你一个圆点,而使用 0 时它们会稍微拉长。
  • 我通过截屏创建了该图像(默认系统范围的快捷方式:⌘⇧4)。据我所知,Xcode 中从来没有内置的捕获工具。
  • James P 的建议非常宝贵。这个错误让我非常心痛和工作。谢谢詹姆斯。我将创建一个半答案,以便人们更清楚地看到它。
  • 我已经用错误解决方法和最新的 Swift 语法更新了我的答案。
【解决方案2】:

上述 Swift 示例的 Objective-C 版本:

UIBezierPath * path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(10.0, 10.0)];
[path addLineToPoint:CGPointMake(290.0, 10.0)];
[path setLineWidth:8.0];
CGFloat dashes[] = { path.lineWidth, path.lineWidth * 2 };
[path setLineDash:dashes count:2 phase:0];
[path setLineCapStyle:kCGLineCapRound];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 20), false, 2);
[path stroke];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

【讨论】:

    【解决方案3】:

    使用与 Swift 3.0 兼容的 UIView 扩展,以下应该可以工作:

    extension UIView {
    
        func addDashedBorder(strokeColor: UIColor, lineWidth: CGFloat) {
            self.layoutIfNeeded()
            let strokeColor = strokeColor.cgColor
    
            let shapeLayer:CAShapeLayer = CAShapeLayer()
            let frameSize = self.frame.size
            let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
    
            shapeLayer.bounds = shapeRect
            shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.strokeColor = strokeColor
            shapeLayer.lineWidth = lineWidth
            shapeLayer.lineJoin = kCALineJoinRound
    
            shapeLayer.lineDashPattern = [5,5] // adjust to your liking
            shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: shapeRect.width, height: shapeRect.height), cornerRadius: self.layer.cornerRadius).cgPath
    
            self.layer.addSublayer(shapeLayer)
        }
    
    }
    

    然后在viewDidLoad 之后运行的函数中,例如viewDidLayoutSubviews,在相关视图上运行addDashedBorder 函数:

    class ViewController: UIViewController {
    
        var someView: UIView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            someView = UIView()
            someView.layer.cornerRadius = 5.0
    
            view.addSubview(someView)
    
            someView.translatesAutoresizingMaskIntoConstraints = false
            someView.widthAnchor.constraint(equalToConstant: 200).isActive = true
            someView.heightAnchor.constraint(equalToConstant: 200).isActive = true
            someView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
            someView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        }
    
        override func viewDidLayoutSubviews() {
            someView.addDashedBorder(strokeColor: UIColor.red, lineWidth: 1.0)
        }
    
    }
    

    【讨论】:

    • 这会创建一条虚线(即矩形),但是如何创建一条虚线(即圆形)?
    【解决方案4】:

    大家好,这个解决方案对我很有效。我找到了某个地方并进行了一些更改以防止控制台警告。

    extension UIImage {
        static func drawDottedImage(width: CGFloat, height: CGFloat, color: UIColor) -> UIImage {
            let path = UIBezierPath()
            path.move(to: CGPoint(x: 1.0, y: 1.0))
            path.addLine(to: CGPoint(x: width, y: 1))
            path.lineWidth = 1.5           
            let dashes: [CGFloat] = [path.lineWidth, path.lineWidth * 5]
            path.setLineDash(dashes, count: 2, phase: 0)
            path.lineCapStyle = .butt
            UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 2)
            color.setStroke()
            path.stroke()
    
            let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
    
            return image
        }
    }
    

    这是结果:

    【讨论】:

    • 我喜欢这种简单的方法
    【解决方案5】:

    我在 rob mayoff 接受的解决方案上做了一些工作,以轻松自定义虚线:

    • 更改每个圆的半径。
    • 更改 2 个圆圈之间的空格数。
    • 更改要生成的模式数量。

    该函数返回一个 UIImage:

    extension UIImage {
    
        class func dottedLine(radius radius: CGFloat, space: CGFloat, numberOfPattern: CGFloat) -> UIImage {
    
    
            let path = UIBezierPath()
            path.moveToPoint(CGPointMake(radius/2, radius/2))
            path.addLineToPoint(CGPointMake((numberOfPattern)*(space+1)*radius, radius/2))
            path.lineWidth = radius
    
            let dashes: [CGFloat] = [path.lineWidth * 0, path.lineWidth * (space+1)]
            path.setLineDash(dashes, count: dashes.count, phase: 0)
            path.lineCapStyle = CGLineCap.Round
    
    
            UIGraphicsBeginImageContextWithOptions(CGSizeMake((numberOfPattern)*(space+1)*radius, radius), false, 1)
            UIColor.whiteColor().setStroke()
            path.stroke()
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
    
            return image
    
        }
    }
    

    这是获取图像的方法:

    UIImage.dottedLine(radius: 100, space: 2, numberOfPattern: 1)
    

    【讨论】:

      【解决方案6】:

      不是一个完整的答案,只是 James P 在评论最喜欢的答案时提出的一个非常重要的问题

      他写道:

      我发现将开启长度设置为 0.01 会给你一个圆点, 而使用 0 时它们会略微拉长。

      例如,

         let dashes: [CGFloat] = [0.001, path.lineWidth * 2]
      

      【讨论】:

        【解决方案7】:

        在 swift 3.1 中,您可以使用以下代码:

        context.setLineCap(.round)
        

        有三种样式:

         /* Line cap styles. */
        
        public enum CGLineCap : Int32 {
        
            case butt
        
            case round
        
            case square
        }
        

        【讨论】:

          【解决方案8】:

          使用下面的代码可以正常工作,

          layer.path = linePath.cgPath
          layer.lineWidth = 3
          layer.lineDashPattern = [1,layer.lineWidth*2] as [NSNumber]
          layer.lineCap = "round"
          

          【讨论】:

            【解决方案9】:

            嘿,回答这个问题可能为时已晚。但是,如果您同意,我想与将来可能会遇到该问题的开发人员分享一种解决该问题的简单方法。所以我猜这是使用@IBDesignable 的最简单的解决方案。你只需要创建那个类

            import UIKit
            
            @IBDesignable class DottedVertical: UIView {
            
                @IBInspectable var dotColor: UIColor = UIColor.red
                @IBInspectable var lowerHalfOnly: Bool = false
            
                override func draw(_ rect: CGRect) {
            
                    // say you want 8 dots, with perfect fenceposting:
                    let totalCount = 8 + 8 - 1
                    let fullHeight = bounds.size.height
                    let width = bounds.size.width
                    let itemLength = fullHeight / CGFloat(totalCount)
            
                    let path = UIBezierPath()
            
                    let beginFromTop = CGFloat(0.0)
                    let top = CGPoint(x: width/2, y: beginFromTop)
                    let bottom = CGPoint(x: width/2, y: fullHeight)
            
                    path.move(to: top)
                    path.addLine(to: bottom)
            
                    path.lineWidth = width
                    //DASHED SIMPLE LINE
                    //let dashes: [CGFloat] = [itemLength, itemLength]
                    //path.setLineDash(dashes, count: dashes.count, phase: 0)
            
                    // for ROUNDED dots, simply change to....
                    let dashes: [CGFloat] = [0.0, itemLength * 1.1]
                    path.lineCapStyle = CGLineCap.round
                    path.setLineDash(dashes, count: dashes.count, phase: 0)
            
                    dotColor.setStroke()
                    path.stroke()
                }
            }
            

            然后像这样将它附加到情节提要中的视图中

            完成后,您可以从这一行 let dashes: [CGFloat] = [0.0, itemLength * 1.1] --> DottedVertical 类中的第 39 行冷自定义层之间的空间。 或者如果您想自定义图层的宽度,您只需从情节提要中编辑线视图宽度

            【讨论】:

              【解决方案10】:

              使用 CAShapeLayer 和 CGMutablePath 创建垂直或水平虚线/点线

                  func drawDottedLine(dashColor:UIColor,linePattern:[NSNumber] = [2,2],lineWidth:CGFloat = 1, orientation: Orientation = .horizontal, shape: Shape = .line) {
                  
                  let shapeLayer = CAShapeLayer()
                  shapeLayer.strokeColor = dashColor.cgColor
                  shapeLayer.lineWidth = lineWidth
                  shapeLayer.lineDashPattern = linePattern 
                  if shape == .round {
                      shapeLayer.lineCap = .round
                  }
                  let path = CGMutablePath()
                  if orientation == .vertical {
                      path.addLines(between: [CGPoint(x: lineWidth/2, y: lineWidth/2),
                                              CGPoint(x: lineWidth/2, y: self.frame.height)])
                  } else {
                      path.addLines(between: [CGPoint(x: lineWidth/2, y: lineWidth/2),
                                              CGPoint(x: self.frame.width, y: lineWidth/2)])
                  }
                  shapeLayer.path = path
                  layer.addSublayer(shapeLayer)
                  
              }
              

              【讨论】:

                【解决方案11】:

                我已经实现了以下代码,以在viewDidAppeartitleLabel (UILabel) 底部添加带点样式的边框:

                CAShapeLayer *shapelayer = [CAShapeLayer layer];
                UIBezierPath *path = [UIBezierPath bezierPath];
                [path moveToPoint:CGPointMake(0.0, titileLabel.frame.size.height-2)];
                [path addLineToPoint:CGPointMake(SCREEN_WIDTH, titileLabel.frame.size.height-2)];
                UIColor *fill = [UIColor colorWithRed:0.80f green:0.80f blue:0.80f alpha:1.00f];
                shapelayer.strokeStart = 0.0;
                shapelayer.strokeColor = fill.CGColor;
                shapelayer.lineWidth = 2.0;
                shapelayer.lineJoin = kCALineJoinRound;
                shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:2],[NSNumber numberWithInt:3 ], nil];
                shapelayer.path = path.CGPath;
                
                [titileLabel.layer addSublayer:shapelayer];
                

                参考:https://gist.github.com/kaiix/4070967

                【讨论】:

                • 当我使用你的代码时,这会产生正方形,而不是圆形。
                • 尝试根据您的要求更改 moveToPoint、addLineToPoint、linewidth 和 lineDashPattern 值
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-04-28
                • 1970-01-01
                • 2022-11-08
                • 2015-02-22
                • 2012-09-23
                • 1970-01-01
                相关资源
                最近更新 更多