Làm cách nào để bạn vẽ một đường theo lập trình từ bộ điều khiển chế độ xem?


82

Tôi có một UIViewController. Làm cách nào để vẽ một đường thẳng trong một trong các chế độ xem được tạo lập trình của nó?


2
Không, bạn không thể. Việc vẽ nên được thực hiện bằng chế độ xem chứ không phải bộ điều khiển. Bạn phải thay đổi thiết kế của mình.
Bryan Chen,

1
@xlc Bạn CÓ THỂ làm điều đó từ VC, như câu trả lời của Rob cho thấy. Nếu bạn thực sự muốn hữu ích, hãy giải thích tác hại của kỹ thuật bezier là gì.
mkc842,

9
"Được đóng lại như không phải là một câu hỏi thực sự." "Rất khó để nói những gì đang được hỏi ở đây." "Không thể được trả lời một cách hợp lý." Thật là một câu chuyện hài hước! Câu hỏi hoàn toàn rõ ràng và Rob dường như không gặp khó khăn gì khi trả lời nó một cách hoàn hảo. Nếu bạn có vấn đề với câu hỏi của tôi, tại sao không giải thích vấn đề của bạn thay vì kết thúc nó với những lý do rõ ràng là sai?
mkc842,

Chúng ta có thể xem một số mã? Tôi đồng ý, đó là một câu hỏi thực sự, nhưng tôi (có thể là chúng tôi) trả lời tốt nhất để mã chứng minh những gì bạn đang nói.
bugmagnet

Câu trả lời:


186

Có hai kỹ thuật phổ biến.

  1. Sử dụng CAShapeLayer:

    • Tạo một UIBezierPath(thay thế tọa độ bằng bất kỳ tọa độ nào bạn muốn):

      UIBezierPath *path = [UIBezierPath bezierPath];
      [path moveToPoint:CGPointMake(10.0, 10.0)];
      [path addLineToPoint:CGPointMake(100.0, 100.0)];
      
    • Tạo một CAShapeLayersử dụng UIBezierPath:

      CAShapeLayer *shapeLayer = [CAShapeLayer layer];
      shapeLayer.path = [path CGPath];
      shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
      shapeLayer.lineWidth = 3.0;
      shapeLayer.fillColor = [[UIColor clearColor] CGColor];
      
    • Thêm nó CAShapeLayervào lớp trong chế độ xem của bạn:

      [self.view.layer addSublayer:shapeLayer];
      

    Trong các phiên bản trước của Xcode, bạn phải thêm QuartzCore.framework theo cách thủ công vào "Liên kết nhị phân với thư viện" của dự án và nhập <QuartzCore/QuartzCore.h>tiêu đề trong tệp .m của bạn, nhưng điều đó không cần thiết nữa (nếu bạn có "Bật mô-đun" và "Liên kết Frameworks Tự động "bật cài đặt xây dựng).

  2. Cách tiếp cận khác là phân lớp UIViewvà sau đó sử dụng lời gọi CoreGraphics trong drawRectphương thức:

    • Tạo một UIViewlớp con và xác định một lớp drawRectvẽ đường của bạn.

      Bạn có thể làm điều này với 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);
      }
      

      Hoặc sử dụng 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];
      }
      
    • Sau đó, bạn có thể sử dụng lớp chế độ xem này làm lớp cơ sở cho NIB / bảng phân cảnh hoặc chế độ xem của mình hoặc bạn có thể đặt bộ điều khiển chế độ xem của mình theo cách lập trình thêm nó làm chế độ xem phụ:

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

Các phiên bản Swift của hai phương pháp trên như sau:

  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 lớp con:

    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()
        }
    
    }
    

    Và thêm nó vào hệ thống phân cấp chế độ xem của bạn:

    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
    

    Ở trên, tôi đang thêm theo PathViewchương trình, nhưng bạn cũng có thể thêm nó qua IB và chỉ cần đặt nó theo pathchương trình.


3
Tuyệt vời. Cảm ơn vì câu trả lời cặn kẽ.
mkc842

@Rob: Điều này hoạt động hoàn toàn tốt. Em muốn hỏi anh là đoạn thẳng AB và AB một góc thì làm thế nào để lấy điểm đầu và điểm cuối trong tình huống đó? Làm ơn giúp tôi.
Manthan

Tôi đã thêm cử chỉ vào chế độ xem của mình và tôi có điểm bắt đầu chạm và điểm di chuyển nếu tôi thêm điểm đó trong nhận xét ở trên, đường thẳng hoạt động bình thường. nhưng khi tôi chạm vào bên cạnh điểm bắt đầu, nó chiếm màu @Rob
Kishore Kumar

Khi sử dụng phương pháp CAShapeLayer bạn có thể (và có thể sẽ muốn) thiết lập giới hạn dòng để làm tròn sao cho dòng trông mịn như họ kết nối,shapeLayer.lineCap = kCALineCapRound;
Albert Renshaw

Hoặc, nếu bạn có nhiều đường dây mà bạn muốn trông mượt mà khi chúng kết nối, bạn cũng có thể có một đường duy nhất UIBezierPathvới các cuộc gọi addLineToPoint/ lặp lại addLine(to:). Sau đó, bạn có thể chọn xem bạn thích shapeLayer.lineJoin = kCALineJoinRoundhoặc kCALineJoinBevelhoặc kCALineJoinMiter.
Rob

15

Tạo một UIView và thêm nó làm chế độ xem phụ của chế độ xem bộ điều khiển chế độ xem của bạn. Bạn có thể sửa đổi chiều cao hoặc chiều rộng của chế độ xem phụ này thành rất nhỏ để nó trông giống như một đường thẳng. Nếu bạn cần vẽ một đường chéo, bạn có thể sửa đổi thuộc tính chuyển đổi chế độ xem phụ.

ví dụ: vẽ đường ngang màu đen. Điều này được gọi từ bên trong triển khai trình điều khiển chế độ xem của bạn

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

Tôi có xu hướng đồng ý rằng đối với một phần tử phân tách (ví dụ: một đường giữa các phần tử giao diện người dùng) thì đây là đường dẫn ít kháng cự nhất. Việc thay thế kết xuất thành một bitmap để trở lại lớp CoreAnimation hoặc triển khai trình xử lý bản vẽ trong một chế độ xem tùy chỉnh có liên quan nhiều hơn. Điều này có lẽ phần cứng cũng tăng tốc tốt hơn, miễn là nó chỉ có một dòng.
marko

4
Tôi tò mò về lý do tại sao đây là một ý tưởng khủng khiếp. Tôi muốn tránh bất kỳ sự cố nào mà giải pháp này có thể gây ra trong mã của riêng tôi.
Jeff Ames

@sangony Tôi cũng sử dụng kỹ thuật này trong mã của mình. Tôi cũng tự hỏi tại sao đây là một ý tưởng khủng khiếp? Xin vui lòng giúp đỡ để giải thích.
Joe Huang

9

Swift 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)

Thật tuyệt vời! Cảm ơn bạn.
Zhanserik

8

Đây là một kỹ thuật thú vị mà bạn có thể thấy hữu ích: Sử dụng các khối để vẽ để tránh phân lớp trong Objective-C

Bao gồm lớp con chế độ xem mục đích chung của bài viết trong dự án của bạn, sau đó đây là loại mã bạn có thể đặt trong bộ điều khiển chế độ xem của mình để tạo chế độ xem nhanh chóng vẽ một đường:

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

Bạn có thể sử dụng UIImageView để vẽ các đường trên.

Tuy nhiên, nó cho phép bỏ qua phân loại phụ. Và vì tôi hơi nghiêng về Đồ họa cốt lõi vẫn có thể sử dụng nó. Bạn chỉ có thể đặt nó vào -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();

Bổ sung cho câu trả lời của Rob, Để có được nhanh chóng, cách tiếp cận thứ ba là sử dụng UIImageView- che đậy nó - quan điểm của xib. (Đó là giao diện UIImageView mặc định khi được kéo trên xib trong xcode 5)

Chúc mừng và +1!


2

Bạn thực sự không nên, nhưng nếu vì lý do nào đó nó có ý nghĩa với bạn, bạn có thể tạo một lớp con của UIView, được gọi là DelegateDrawViewví dụ, lấy một ủy quyền thực hiện một phương thức như

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

và sau đó trong các phương thức - [DelegateDrawView drawRect:]bạn nên gọi phương thức ủy nhiệm của mình.

Nhưng tại sao bạn lại muốn đặt mã chế độ xem trong bộ điều khiển của mình.

Tốt hơn hết bạn nên tạo một lớp con của UIView, lớp này vẽ một đường thẳng giữa hai góc của nó, bạn có thể có một thuộc tính để đặt hai thuộc tính đó và sau đó định vị dạng xem bạn muốn nó từ bộ điều khiển xem.


1

Để vẽ bên trong khung nhìn của bạn rất đơn giản, @ Mr.ROB cho biết 2 phương pháp tôi đã thực hiện phương pháp đầu tiên.

Chỉ cần sao chép, dán mã vào nơi bạn muốn.

-(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);
}

Nó sẽ vẽ như một đường thẳng, nơi ngón tay di chuyển

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.