【问题标题】:UIView with a Dashed line带有虚线的 UIView
【发布时间】:2012-08-18 23:39:39
【问题描述】:

我有什么:

要创建这一行,我基本上有一个UIView,我执行以下操作:

void setLayerToLineFromAToB(CALayer *layer, CGPoint a, CGPoint b, CGFloat lineWidth)
{
    CGPoint center = { 0.5 * (a.x + b.x), 0.5 * (a.y + b.y) };
    CGFloat length = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
    CGFloat angle = atan2(a.y - b.y, a.x - b.x);

    layer.position = center;
    layer.bounds = (CGRect) { {0, 0}, { length + lineWidth, lineWidth } };
    layer.transform = CATransform3DMakeRotation(angle, 0, 0, 1);
}

注意:此代码是在 stackoverflow 上找到的,因此如果有人能给我提供参考,我将不胜感激。

我想要什么:

好的,所以我需要的“唯一”事情是在UIView 上创建此模式。我知道我可以使用 Quartz2D 来做到这一点(可以找到一个简单的方法来做到这一点here)。但我想通过操纵CALayer 而不是使用draw 方法来做到这一点。为什么?由于我在 UIView 上进行的转换,我无法使用 draw 方法正确绘制。

编辑 1:

只是为了说明我的问题:

通常你拥有的是UIView,然后你基本上只是在其中画一些东西(在这种情况下是一条简单的线)。我发现摆脱“灰色”区域的解决方案是,而不是绘制一些东西,只是改变UIView 本身。它工作得很好,如果你想要一条完全填充的线,当你想要一条虚线时问题就来了。

【问题讨论】:

  • 为什么不用 Quartz2D 在图层上绘制这个然后变换图层?!
  • 你在说什么,基本上是在draw方法上画出来然后应用变换?
  • 是的,这就是我的意思......所以这就像你画一个“图像”然后只是转换这个“图像”
  • @lukaswelte 检查我的编辑,看看你说的是否有道理。
  • 摆脱灰色背景?为什么不只是在 drawRect 中画线,然后转换你的 uiview 将完全符合你想要的技巧......而这对于你之前在视图上绘制的任何形状

标签: iphone ios uiview calayer quartz-2d


【解决方案1】:

更新 Swift 5UIBezierPath

对于那些使用UIBezierPath 而不是CAShapeLayer 的人,这里是实现它的方法

class MyView: UIView {

    override func draw(_ rect: CGRect) {

        let path = UIBezierPath()
        // >> define the pattern & apply it 
        let dashPattern: [CGFloat] = [4.0, 4.0]
        path.setLineDash(dashPattern, count: dashPattern.count, phase: 0)
        // <<
        path.lineWidth = 1
        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: 100, y: 100))
        path.stroke()

    }
}

正如该线程中多次所说,您可以使用patternphase 来实现复杂的虚线。

希望对你有帮助

【讨论】:

    【解决方案2】:

    检查 UIBezierPath setLineDash:count:phase: method:

    - (void)setLineDash:(const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase` method. 
    

    这允许您绘制虚线。

    1. 首先添加CAShapeLayer。将其作为子层添加到您的UIView。它有一个path 属性。
    2. 现在创建一个UIBezierPath 的对象。使用setLineDash画线。

    例如:

     UIBezierPath *path = [UIBezierPath bezierPath];
     //draw a line
     [path moveToPoint:yourStartPoint]; //add yourStartPoint here
     [path addLineToPoint:yourEndPoint];// add yourEndPoint here
     [path stroke];
    
     CGFloat dashPattern[] = {2.0f,6.0f,4.0f,2.0f}; //make your pattern here
     [path setLineDash:dashPattern count:4 phase:3];
    
     UIColor *fill = [UIColor blueColor];
     shapelayer.strokeStart = 0.0;
     shapelayer.strokeColor = fill.CGColor;
     shapelayer.lineWidth = 5.0;
     shapelayer.lineJoin = kCALineJoinMiter;
     shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:10],[NSNumber numberWithInt:7], nil];
     shapelayer.lineDashPhase = 3.0f;
     shapelayer.path = path.CGPath;
    

    注意:此答案提供了一个提示,以便您可以根据自己的要求即兴创作。

    【讨论】:

    • 您是在应用转换之前还是之后这样做?
    • 我没有应用转换。杰克男孩你已经创建了像一条线一样的图层。但实际上你还没有画线
    • 我在 UIViewinitWithFrame: 方法上执行此操作。我收到这种错误:&lt;Error&gt;: CGContextSaveGState: invalid context 0x0.
    • float dashPattern[] = {2,6,4,2} 应该是CGFloat dashPattern[] = {2,6,4,2}
    【解决方案3】:

    Swift4 • Xcode 9 中的短划线

    创建一个 CAShapeLayer 并使用 lineDashPattern

    extension UIView {
    
        func addDashedBorder() {
            //Create a CAShapeLayer
            let shapeLayer = CAShapeLayer()
            shapeLayer.strokeColor = UIColor.red.cgColor
            shapeLayer.lineWidth = 2
            // passing an array with the values [2,3] sets a dash pattern that alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment
            shapeLayer.lineDashPattern = [2,3]
    
            let path = CGMutablePath()
            path.addLines(between: [CGPoint(x: 0, y: 0),
                                    CGPoint(x: self.frame.width, y: 0)])
            shapeLayer.path = path
            layer.addSublayer(shapeLayer)
        }
    }
    

    用法:

    dashView.addDashedBorder()
    

    输出:

    【讨论】:

    • 如何使线条全宽?
    • 你为什么不用UIView
    【解决方案4】:

    这是 Alexandre G 的回答 https://stackoverflow.com/a/38194152/1800489 的 Swift 3 版本

    extension UIView {
    
            func addDashedLine(color: UIColor = .lightGray) {
                layer.sublayers?.filter({ $0.name == "DashedTopLine" }).map({ $0.removeFromSuperlayer() })
                backgroundColor = .clear
    
                let shapeLayer = CAShapeLayer()
                shapeLayer.name = "DashedTopLine"
                shapeLayer.bounds = bounds
                shapeLayer.position = CGPoint(x: frame.width / 2, y: frame.height / 2)
                shapeLayer.fillColor = UIColor.clear.cgColor
                shapeLayer.strokeColor = color.cgColor
                shapeLayer.lineWidth = 1
                shapeLayer.lineJoin = kCALineJoinRound
                shapeLayer.lineDashPattern = [4, 4]
    
                let path = CGMutablePath()
                path.move(to: CGPoint.zero)
                path.addLine(to: CGPoint(x: frame.width, y: 0))
                shapeLayer.path = path
    
                layer.addSublayer(shapeLayer)
            }
    }
    

    【讨论】:

      【解决方案5】:

      接受的答案有坐标问题。这条线将在下方绘制一段距离。而且我无法弄清楚它在 Y 坐标上增加的原因和距离。

      有一种方法可以用正确的坐标绘制虚线:

      -(void)drawRect:(CGRect)rect
      {
           CGContextBeginPath(cx);
           CGContextRef cx = UIGraphicsGetCurrentContext();
           CGContextSetLineWidth(cx, _thickness);
           CGContextSetStrokeColorWithColor(cx, _color.CGColor);
      
           CGFloat dash[] = {_dashedLength,_dashedGap};
           CGContextSetLineDash(cx, 0, dash, 2); // nb "2" == ra count
      //    CGContextSetLineCap(cx, kCGLineCapRound);
      
           CGContextMoveToPoint(cx, 0, _thickness);
           CGContextAddLineToPoint(cx, self.bounds.size.width, _thickness);
           CGContextStrokePath(cx);
           CGContextClosePath(cx);
      }
      

      这个答案来自Draw dotted (not dashed!) line, with IBDesignable in 2017。 当你想要黑色虚线时,不要忘记将背景颜色设置为白色!!默认情况下,视图的背景颜色为黑色,线条颜色也是黑色,所以我认为它是一条实线。我花了半天时间才知道。 T_T

      【讨论】:

      • 这对您来说可能为时已晚,但您可以查看我对@Shaheen Ghiassy 回答的评论
      • 花了我半小时T_T,感谢您的警告,它并没有花费我更多。
      【解决方案6】:

      斯威夫特 2.2

      把这个放在这里以节省其他人的时间..

      extension UIView {
          func addDashedLine(color: UIColor = UIColor.lightGrayColor()) {
              layer.sublayers?.filter({ $0.name == "DashedTopLine" }).map({ $0.removeFromSuperlayer() })
              self.backgroundColor = UIColor.clearColor()
              let cgColor = color.CGColor
      
              let shapeLayer: CAShapeLayer = CAShapeLayer()
              let frameSize = self.frame.size
              let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
      
              shapeLayer.name = "DashedTopLine"
              shapeLayer.bounds = shapeRect
              shapeLayer.position = CGPoint(x: frameSize.width / 2, y: frameSize.height / 2)
              shapeLayer.fillColor = UIColor.clearColor().CGColor
              shapeLayer.strokeColor = cgColor
              shapeLayer.lineWidth = 1
              shapeLayer.lineJoin = kCALineJoinRound
              shapeLayer.lineDashPattern = [4, 4]
      
              let path: CGMutablePathRef = CGPathCreateMutable()
              CGPathMoveToPoint(path, nil, 0, 0)
              CGPathAddLineToPoint(path, nil, self.frame.width, 0)
              shapeLayer.path = path
      
              self.layer.addSublayer(shapeLayer)
          }
      }
      

      【讨论】:

      • 只是一个提示:你可以使用.forEach() 代替map,这样编译器就不会给出map 未使用结果的警告。
      【解决方案7】:

      首先要感谢 RuiAAPeres 和 Prince,我只是将他们的答案封装到 UIView 对象中,其他人可以将其放入他们的项目中并使用

      #import <UIKit/UIKit.h>
      
      /**
       *  Simple UIView for a dotted line
       */
      @interface H3DottedLine : UIView
      
      /**
       *  Set the line's thickness
       */
      @property (nonatomic, assign) CGFloat thickness;
      
      /**
       *  Set the line's color
       */
      @property (nonatomic, copy) UIColor *color;
      
      /**
       *  Set the length of the dash
       */
      @property (nonatomic, assign) CGFloat dashedLength;
      
      /**
       *  Set the gap between dashes
       */
      @property (nonatomic, assign) CGFloat dashedGap;
      
      @end
      
      
      
      
      @implementation H3DottedLine
      
      #pragma mark - Object Lifecycle
      
      - (instancetype)init {
          self = [super init];
      
          if (self) {
              // Set Default Values
              _thickness = 1.0f;
              _color = [UIColor whiteColor];
              _dashedGap = 1.0f;
              _dashedLength = 5.0f;
          }
      
          return self;
      }
      
      #pragma mark - View Lifecycle
      
      - (void)layoutSubviews {
          // Note, this object draws a straight line. If you wanted the line at an angle you simply need to adjust the start and/or end point here.
          [self updateLineStartingAt:self.frame.origin andEndPoint:CGPointMake(self.frame.origin.x+self.frame.size.width, self.frame.origin.y)];
      }
      
      #pragma mark - Setters
      
      - (void)setThickness:(CGFloat)thickness {
          _thickness = thickness;
          [self setNeedsLayout];
      }
      
      - (void)setColor:(UIColor *)color {
          _color = [color copy];
          [self setNeedsLayout];
      }
      
      - (void)setDashedGap:(CGFloat)dashedGap {
          _dashedGap = dashedGap;
          [self setNeedsLayout];
      }
      
      - (void)setDashedLength:(CGFloat)dashedLength {
          _dashedLength = dashedLength;
          [self setNeedsLayout];
      }
      
      #pragma mark - Draw Methods
      
      -(void)updateLineStartingAt:(CGPoint)beginPoint andEndPoint:(CGPoint)endPoint {
      
          // Important, otherwise we will be adding multiple sub layers
          if ([[[self layer] sublayers] objectAtIndex:0]) {
              self.layer.sublayers = nil;
          }
      
          CAShapeLayer *shapeLayer = [CAShapeLayer layer];
          [shapeLayer setBounds:self.bounds];
          [shapeLayer setPosition:self.center];
          [shapeLayer setFillColor:[UIColor clearColor].CGColor];
          [shapeLayer setStrokeColor:self.color.CGColor];
          [shapeLayer setLineWidth:self.thickness];
          [shapeLayer setLineJoin:kCALineJoinRound];
          [shapeLayer setLineDashPattern:@[@(self.dashedLength), @(self.dashedGap)]];
      
          // Setup the path
          CGMutablePathRef path = CGPathCreateMutable();
          CGPathMoveToPoint(path, NULL, beginPoint.x, beginPoint.y);
          CGPathAddLineToPoint(path, NULL, endPoint.x, endPoint.y);
      
          [shapeLayer setPath:path];
          CGPathRelease(path);
      
          [[self layer] addSublayer:shapeLayer];
      }
      
      @end
      

      【讨论】:

      • 适用于无法完成这门课程的人。尝试将 self.frame 的所有实例更改为 self.bounds 并将 [shapeLayer setPosition:self.center] 设置为 [shapeLayer setPosition:CGPointZero]
      【解决方案8】:

      注意:Prince 的代码确实帮了我很大的忙,所以我会给他+10 的提示。但最后,我添加了我自己的代码。我还将为其添加一些上下文,以便对未来的读者有用


      最后的代码是这样的:

      -(void)updateLine{
      
            // Important, otherwise we will be adding multiple sub layers
            if ([[[self layer] sublayers] objectAtIndex:0])
              {
                  self.layer.sublayers = nil;
              }
      
              CAShapeLayer *shapeLayer = [CAShapeLayer layer];
              [shapeLayer setBounds:self.bounds];
              [shapeLayer setPosition:self.center];
              [shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
              [shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
              [shapeLayer setLineWidth:3.0f];
              [shapeLayer setLineJoin:kCALineJoinRound];
              [shapeLayer setLineDashPattern:
              [NSArray arrayWithObjects:[NSNumber numberWithInt:10],
              [NSNumber numberWithInt:5],nil]];
      
              // Setup the path
              CGMutablePathRef path = CGPathCreateMutable();
              CGPathMoveToPoint(path, NULL, beginPoint.center.x, beginPoint.center.y);
              CGPathAddLineToPoint(path, NULL, endPoint.center.x, endPoint.center.y);
      
              [shapeLayer setPath:path];
              CGPathRelease(path);
      
              [[self layer] addSublayer:shapeLayer];
      }
      

      在我的例子中,用户可以使用 KVO 移动 beginPoint 和 endPoint。因此,当其中一个移动时:

      -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
      {
          if ([keyPath isEqual:@"position"])
          {
              [self updateLine];
          }
      }
      

      我确实玩了很多普林斯的代码。我尝试了draw: 方法,它在虚线之间添加了一条细线(有点奇怪......),我也尝试了initWithFrame:。他的代码本身,未经任何修改,会在控制台上给我这种错误:

      <Error>: CGContextSaveGState: invalid context 0x0
      <Error>: CGContextSetLineWidth: invalid context 0x0
      <Error>: CGContextSetLineJoin: invalid context 0x0
      <Error>: CGContextSetLineCap: invalid context 0x0
      <Error>: CGContextSetMiterLimit: invalid context 0x0
      <Error>: CGContextSetFlatness: invalid context 0x0
      <Error>: CGContextAddPath: invalid context 0x0
      <Error>: CGContextDrawPath: invalid context 0x0
      <Error>: CGContextRestoreGState: invalid context 0x0
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-25
        • 1970-01-01
        • 1970-01-01
        • 2012-11-20
        • 1970-01-01
        • 2019-05-09
        • 2015-12-06
        • 1970-01-01
        相关资源
        最近更新 更多