Як ви програмуєте лінію програмно з контролера перегляду?


82

У мене є UIViewController. Як провести лінію в одному із програмно створених подань?


2
Ні, ти не можеш. Малюнок повинен виконуватися за допомогою перегляду, а не контролера. Вам доведеться змінити дизайн.
Брайан Чен,

1
@xlc Ви МОЖЕТЕ це зробити з ВК, як показує відповідь Роба. Якщо ви насправді хочете бути корисними, поясніть, яка шкода в техніці Безьє.
mkc842

Чи можемо ми побачити якийсь код? Я згоден, це справжнє запитання, але я (можливо, ми) найкраще реагую на код, що демонструє те, про що ви говорите.
bugmagnet

Відповіді:


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-файл, але це вже не потрібно (якщо у вас є "Увімкнути модулі" та "Посилання Frameworks Automatically "налаштування побудови ввімкнено).

  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 / розкадровки або перегляду, або ви можете зробити так, щоб ваш контролер перегляду програмно додав його як підпрогляд:

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

Швидкі передачі двох вищевказаних підходів є такими:

  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програмно.


@Rob: Це працює чудово. Я хочу запитати вас, що я маю лінійну відстань AB і кут від AB, як тоді отримати початкові та кінцеві точки в цій ситуації? Будь ласка, допоможи мені.
Мантан

я додав uigesture на мій погляд, і у мене є контакт відправної точки і рухається точка , якщо я додам , що в вище коментарю прямому працюю належним чином. коли я помацати на стороні від початку самої її займає колір @Rob точку
Кишор Кумар

Використовуючи метод CAShapeLayer, ви можете (і, можливо, захочете) встановити закруглені лінії так, щоб лінії виглядали плавними, коли вони з'єднуються,shapeLayer.lineCap = kCALineCapRound;
Альберт Реншоу,

Або, якщо у вас є кілька ліній, які ви хочете виглядати гладко, коли вони з’єднуються, ви також можете мати одну UIBezierPath, з повторними addLineToPoint/ addLine(to:)дзвінками. Тоді ви можете вибрати, чи віддаєте перевагу, shapeLayer.lineJoin = kCALineJoinRoundчи kCALineJoinBevelабо kCALineJoinMiter.
Роб

Дякую за фрагмент коду, це заощадило мені досить багато часу. Якщо ви маєте намір намалювати динамічний вигляд, реагуючи на деякі події, вам потрібно буде очистити підшар на початку розіграшу цим: self.layer.sublayers = nil
Вільмір

15

Створіть UIView і додайте його як підпрогляд подання контролера перегляду. Ви можете змінити висоту або ширину цього підпрогляду, щоб вони були дуже малими, щоб вони мали вигляд лінії. Якщо вам потрібно намалювати діагональну лінію, ви можете змінити властивість перегляду підпоглядів.

наприклад, намалювати чорну горизонтальну лінію. Це викликається в рамках реалізації вашого контролера подання

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

Я був би схильний погодитись, що для розділювального елемента (наприклад, лінії між елементами інтерфейсу) це шлях найменшого опору. Альтернатива або рендерингу в растрове зображення для підтримки рівня CoreAnimation, або реалізації обробника малювання у власному поданні набагато більше задіяна. Це, ймовірно, апаратне забезпечення також прискорюється краще, за умови, що це лише одна лінія.
Марко

4
Мені цікаво міркувати, чому це жахлива ідея. Я хотів би уникнути будь-яких проблем, які це рішення може спричинити у власному коді.
Джефф Еймс,

@sangony Я теж використовую цю техніку у своєму коді. Мені також цікаво, чому це жахлива ідея? Будь ласка, допоможіть пояснити.
Джо Хуан,

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)

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

Додаток до відповіді Роба: Для UIImageViewшвидкого вибору третім підходом є використання - прикриття нею - подання xib. (Це зовнішній вигляд UIImageView за замовчуванням при перетягуванні на xib у xcode 5)

Вітаємо та +1!


2

Насправді не слід, але якщо з якихось причин це для вас має сенс, ви можете створити підклас UIView, який називається, DelegateDrawViewнаприклад, який приймає делегата, який реалізує такий метод, як

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

а потім у методах - [DelegateDrawView drawRect:]вам слід викликати метод делегата.

Але чому ви хочете помістити код перегляду у свій контролер.

Вам краще створити підклас UIView, який проводить лінію між двома його кутами, у вас може бути властивість встановлювати, які два, а потім розташувати подання там, де ви хочете, від контролера перегляду.


1

Малювати всередині свого зору дуже просто, @ Mr.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);
}

Він буде намальований як лінія, куди рухається палець

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.