如何从视图控制器以编程方式画一条线?


82

我有一个UIViewController。如何在其以编程方式创建的视图之一中画线?


2
不,你不能。绘制应通过视图而非控制器完成。您必须更改设计。
布赖恩·陈

1
如Rob的回答所示,您可以从VC进行@xlc。如果您确实想提供帮助,请说明贝塞尔曲线技术的危害是什么。
mkc842

9
“这不是一个真正的问题。” “很难说出这里要问的是什么。” “无法合理回答。” 真是笑话!这个问题很清楚,Rob回答完美似乎没有任何困难。如果您对我的问题有疑问,为什么不解释问题而不是以明显错误的理由将其解决?
mkc842

我们可以看到一些代码吗?我同意,这是一个真实的问题,但是我(也许我们)对显示您所谈论内容的代码做出最佳反应。
bugmagnet 2014年

Answers:


186

有两种常用技术。

  1. 使用CAShapeLayer

    • 创建一个UIBezierPath(用所需的坐标替换坐标):

      UIBezierPath *path = [UIBezierPath bezierPath];
      [path moveToPoint:CGPointMake(10.0, 10.0)];
      [path addLineToPoint:CGPointMake(100.0, 100.0)];
      
    • 创建一个CAShapeLayer使用该代码的UIBezierPath

      CAShapeLayer *shapeLayer = [CAShapeLayer layer];
      shapeLayer.path = [path CGPath];
      shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
      shapeLayer.lineWidth = 3.0;
      shapeLayer.fillColor = [[UIColor clearColor] CGColor];
      
    • 将其添加CAShapeLayer到视图的图层:

      [self.view.layer addSublayer:shapeLayer];
      

    在以前版本的Xcode中,您必须手动将QuartzCore.framework添加到项目的“使用库链接二进制<QuartzCore/QuartzCore.h>文件,并将标头导入到.m文件中,但这不再是必须的(如果您具有“启用模块”和“链接”自动打开框架”构建设置)。

  2. 另一种方法是子类化UIView,然后在该方法中使用CoreGraphics调用drawRect

    • 创建一个UIView子类并定义一个drawRect画线的子类。

      您可以使用Core Graphics执行此操作:

      - (void)drawRect:(CGRect)rect {
          CGContextRef context = UIGraphicsGetCurrentContext();
      
          CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
          CGContextSetLineWidth(context, 3.0);
          CGContextMoveToPoint(context, 10.0, 10.0);
          CGContextAddLineToPoint(context, 100.0, 100.0);
          CGContextDrawPath(context, kCGPathStroke);
      }
      

      或使用UIKit

      - (void)drawRect:(CGRect)rect {
          UIBezierPath *path = [UIBezierPath bezierPath];
          [path moveToPoint:CGPointMake(10.0, 10.0)];
          [path addLineToPoint:CGPointMake(100.0, 100.0)];
          path.lineWidth = 3;
          [[UIColor blueColor] setStroke];
          [path stroke];
      }
      
    • 然后,您可以将此视图类用作NIB / storyboard或视图的基类,或者可以让视图控制器以编程方式将其添加为子视图:

      PathView *pathView = [[PathView alloc] initWithFrame:self.view.bounds];
      pathView.backgroundColor = [UIColor clearColor];
      
      [self.view addSubview: pathView];
      

上面两种方法的Swift演绎​​如下:

  1. CAShapeLayer

    // create path
    
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 10, y: 10))
    path.addLine(to: CGPoint(x: 100, y: 100))
    
    // Create a `CAShapeLayer` that uses that `UIBezierPath`:
    
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = path.cgPath
    shapeLayer.strokeColor = UIColor.blue.cgColor
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.lineWidth = 3
    
    // Add that `CAShapeLayer` to your view's layer:
    
    view.layer.addSublayer(shapeLayer)
    
  2. UIView 子类:

    class PathView: UIView {
    
        var path: UIBezierPath?           { didSet { setNeedsDisplay() } }
        var pathColor: UIColor = .blue    { didSet { setNeedsDisplay() } }
    
        override func draw(_ rect: CGRect) {
            // stroke the path
    
            pathColor.setStroke()
            path?.stroke()
        }
    
    }
    

    并将其添加到您的视图层次结构:

    let pathView = PathView()
    pathView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(pathView)
    
    NSLayoutConstraint.activate([
        pathView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        pathView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        pathView.topAnchor.constraint(equalTo: view.topAnchor),
        pathView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
    ])
    
    pathView.backgroundColor = .clear
    
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 10, y: 10))
    path.addLine(to: CGPoint(x: 100, y: 100))
    path.lineWidth = 3
    
    pathView.path = path
    

    上面,我正在以PathView编程方式添加,但是您也可以通过IB添加它,并且只需以path编程方式进行设置即可。


3
太棒了 感谢您的详尽回答。
mkc842

@Rob:这很好用。我想问你一个线距AB和一个角度AB,那么在这种情况下如何获得起点和终点?请帮我。
曼森2015年

我在视图中添加了ugesture,并且如果我在上述注释直线中添加了该点,则可以触及起点和移动点。直线工作正常。但是当我从起点本身触摸侧面时,其占据颜色@Rob
Kishore Kumar

当使用CAShapeLayer方法,你可以(并且很可能将要)设置管线盖到圆形,使线条看起来光滑,因为它们连接起来,shapeLayer.lineCap = kCALineCapRound;
阿尔伯特·伦肖

或者,如果您有多条希望在连接时看起来平滑的线,则也可以有一条UIBezierPath重复的addLineToPoint/addLine(to:)调用。然后,您可以选择喜欢 shapeLayer.lineJoin = kCALineJoinRound还是kCALineJoinBevelkCALineJoinMiter
罗布(Rob)2009年

15

创建一个UIView并将其添加为视图控制器视图的子视图。您可以将该子视图的高度或宽度修改得很小,以使其看起来像一条线。如果需要绘制对角线,则可以修改子视图的transform属性。

例如画一条黑色的水平线。这是在您的视图控制器的实现中调用的

UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.frame.size.width, 1)];
lineView.backgroundColor = [UIColor blackColor];
[self.view addSubview:lineView];

我倾向于同意,对于分隔符元素(例如,UI元素之间的线),这是阻力最小的路径。渲染到位图以支持CoreAnimation层或在自定义视图中实现绘图处理程序的替代方案要涉及得多。如果仅一条线,则该硬件的加速也可能会更好。
marko

4
我对为什么这是一个糟糕的主意感到好奇。我想避免此解决方案可能在我自己的代码中引起的任何问题。
杰夫·埃姆斯

@sangony我在代码中也使用了这种技术。我也想知道为什么这是一个糟糕的主意?请帮忙解释。
Joe Huang

9

斯威夫特3:

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

let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 3.0

view.layer.addSublayer(shapeLayer)

这很棒!谢谢。
Zhanserik '17

8

这是一个很酷的技术,您可能会发现它有用:使用绘图块避免在Objective-C中进行子类化

在您的项目中包含本文的通用视图子类,那么您可以将这种代码放入视图控制器中,以便动态绘制视图以绘制线条:

DrawView* drawableView = [[[DrawView alloc] initWithFrame:CGRectMake(0,0,320,50)] autorelease];
drawableView.drawBlock = ^(UIView* v,CGContextRef context)
{
  CGPoint startPoint = CGPointMake(0,v.bounds.size.height-1);
  CGPoint endPoint = CGPointMake(v.bounds.size.width,v.bounds.size.height-1);

  CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
  CGContextSetLineWidth(context, 1);
  CGContextMoveToPoint(context, startPoint.x + 0.5, startPoint.y + 0.5);
  CGContextAddLineToPoint(context, endPoint.x + 0.5, endPoint.y + 0.5);
  CGContextStrokePath(context);
};
[self.view addSubview:drawableView];

6

您可以使用UIImageView在其上绘制线条。

但是,它允许跳过子分类。而且由于我不太喜欢Core Graphics,所以仍然可以使用它。您可以将其放入-ViewDidLoad

  UIGraphicsBeginImageContext(self.view.frame.size);
  [self.myImageView.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
  CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
  CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);

  CGContextMoveToPoint(UIGraphicsGetCurrentContext(), 50, 50);
  CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), 200, 200);
  CGContextStrokePath(UIGraphicsGetCurrentContext());
  CGContextFlush(UIGraphicsGetCurrentContext());
  self.myImageView.image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

除了Rob的答案外,第三种方法是使用UIImageView-掩盖它-xib的视图。(这是在xcode 5中的xib上拖动时的默认UIImageView外观)

干杯,+ 1!


2

您本不应该这样,但是如果出于某种原因对您有意义,则可以创建UIView的子类(DelegateDrawView例如,称为),该子类需要一个委托,该委托实现了类似

- (void)delegateDrawView:(DelegateDrawView *)aDelegateDrawView drawRect:(NSRect)dirtyRect

然后在方法中-[DelegateDrawView drawRect:]您应该调用委托方法。

但是,为什么要在控制器中放置视图代码。

最好创建一个UIView的子类,该子类在它的两个角之间画一条线,可以有一个属性来设置哪两个,然后将其放置在视图控制器中所需的位置。


1

要画出您的视图非常简单,@ ROB先生说2种方法是我采用的第一种方法。

只需将代码粘贴到您想要的位置即可。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
     startingPoint = [touch locationInView:self.view];

    NSLog(@"Touch starting point = x : %f Touch Starting Point = y : %f", touchPoint.x, touchPoint.y);
}
-(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{

}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
     touchPoint = [touch locationInView:self.view];

    NSLog(@"Touch end point =x : %f Touch end point =y : %f", touchPoint.x, touchPoint.y);
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [[event allTouches] anyObject];
    touchPoint = [touch locationInView:self.view];
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(touchPoint.x,touchPoint.y)];
    [path addLineToPoint:CGPointMake(startingPoint.x,startingPoint.y)];
    startingPoint=touchPoint;
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = [path CGPath];
    shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
    shapeLayer.lineWidth = 3.0;
    shapeLayer.fillColor = [[UIColor redColor] CGColor];
    [self.view.layer addSublayer:shapeLayer];

    NSLog(@"Touch moving point =x : %f Touch moving point =y : %f", touchPoint.x, touchPoint.y);
    [self.view setNeedsDisplay];


}
- (void)tapGestureRecognizer:(UIGestureRecognizer *)recognizer {
    CGPoint tappedPoint = [recognizer locationInView:self.view];
    CGFloat xCoordinate = tappedPoint.x;
    CGFloat yCoordinate = tappedPoint.y;

    NSLog(@"Touch Using UITapGestureRecognizer x : %f y : %f", xCoordinate, yCoordinate);
}

它会画一条线,手指移动

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.