如何以编程方式从视图控制器绘制线?

我有一个 UIViewController。如何在它的程序创建的视图之一中画一条线?

71711 次浏览

创建一个 UIView 并将其添加为视图控制器视图的子视图。您可以将这个子视图的高度或宽度修改为非常小,以便它看起来像一条线。如果需要绘制对角线,可以修改 subviews 转换属性。

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

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

有两种常见的技巧。

  1. 使用 CAShapeLayer:

    • 创建一个 UIBezierPath(用你想要的任何东西替换坐标) :

      UIBezierPath *path = [UIBezierPath bezierPath];
      [path moveToPoint:CGPointMake(10.0, 10.0)];
      [path addLineToPoint:CGPointMake(100.0, 100.0)];
      
    • Create a CAShapeLayer that uses that UIBezierPath:

      CAShapeLayer *shapeLayer = [CAShapeLayer layer];
      shapeLayer.path = [path CGPath];
      shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
      shapeLayer.lineWidth = 3.0;
      shapeLayer.fillColor = [[UIColor clearColor] CGColor];
      
    • Add that CAShapeLayer to your view's layer:

      [self.view.layer addSublayer:shapeLayer];
      

    In previous versions of Xcode, you had to manually add QuartzCore.framework to your project's "Link Binary with Libraries" and import the <QuartzCore/QuartzCore.h> header in your .m file, but that's not necessary anymore (if you have the "Enable Modules" and "Link Frameworks Automatically" build settings turned on).

  2. The other approach is to subclass UIView and then use CoreGraphics calls in the drawRect method:

    • Create a UIView subclass and define a drawRect that draws your line.

      You can do this with 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];
      }
      
    • Then you can either use this view class as the base class for your NIB/storyboard or view, or you can have your view controller programmatically add it as a subview:

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

The Swift renditions of the two above approaches are as follows:

  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 subclass:

    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

实际上不应该这样做,但是如果出于某种原因它对您有意义,您可以创建一个 UIView 的子类,例如称为 DelegateDrawView,它接受一个委托,该委托实现一个方法,如

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

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

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

你最好创建一个 UIView 的子类,在它的两个角之间画一条线,你可以有一个属性来设置哪两个,然后从视图控制器中将视图定位到你想要的位置。

这里有一个你可能会发现有用的很酷的技术: 在 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];

可以使用 UIImageView 在。

但是,它允许跳过子类化。由于我几乎不倾向于核心图形仍然可以使用它。你可以把它放进 -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!

在你的视图里面画画是很简单的,@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);
}

手指移动的地方会画一条线

斯威夫特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)

斯威夫特5.4

使用高度为1或2点的 UIView,并将其添加为视图控制器视图的子视图。

class Separator: UIView {


let line = UIView()


override init(frame: CGRect) {
super.init(frame: frame)
configure()
}




required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}


private func configure() {
backgroundColor = .red


addSubview(line)
line.translatesAutoresizingMaskIntoConstraints = false
line.backgroundColor = .secondaryLabelColor


NSLayoutConstraint.activate([
line.centerYAnchor.constraint(equalTo: self.centerYAnchor),
line.centerXAnchor.constraint(equalTo: self.centerXAnchor),
line.heightAnchor.constraint(equalToConstant: Pad.separatorHeight),
line.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.8 )
])
}
}

然后将其添加到视图控制器中:


let separator = Separator()
view.addSubview(separator)
separator.translatesAutoresizingMaskIntoConstraints = false


NSLayoutConstraint.activate([
separator.trailingAnchor.constraint(equalTo: view.trailingAnchor),
separator.topAnchor.constraint(equalTo: view.bottomAnchor),
separator.leadingAnchor.constraint(equalTo: view.leadingAnchor),
separator.heightAnchor.constraint(equalToConstant: 72.0)
])