Bir görünüm denetleyicisinden programlı olarak nasıl çizgi çizersiniz?


82

Bir UIViewController. Programlı olarak oluşturulmuş görünümlerinden birinde nasıl çizgi çizerim?


2
Hayır yapamazsın. Çizim kontrolörle değil görünümle yapılmalıdır. Tasarımınızı değiştirmelisiniz.
Bryan Chen

1
@xlc Rob'un cevabının gösterdiği gibi, bunu bir VC'den YAPABİLİRSİNİZ. Gerçekten yardımcı olmak istiyorsanız, bezier tekniğinin zararının ne olduğunu açıklayın.
mkc842

Biraz kod görebilir miyiz? Katılıyorum, bu gerçek bir soru, ama ben (belki biz) neden bahsettiğinizi gösteren koda en iyi yanıt veriyorum.
bugmagnet

Yanıtlar:


186

İki yaygın teknik vardır.

  1. Kullanarak CAShapeLayer:

    • Bir oluşturun UIBezierPath(koordinatları istediğiniz gibi değiştirin):

      UIBezierPath *path = [UIBezierPath bezierPath];
      [path moveToPoint:CGPointMake(10.0, 10.0)];
      [path addLineToPoint:CGPointMake(100.0, 100.0)];
      
    • Bir oluşturun CAShapeLayero kullanımları o UIBezierPath:

      CAShapeLayer *shapeLayer = [CAShapeLayer layer];
      shapeLayer.path = [path CGPath];
      shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
      shapeLayer.lineWidth = 3.0;
      shapeLayer.fillColor = [[UIColor clearColor] CGColor];
      
    • Bunu CAShapeLayergörünümünüzün katmanına ekleyin:

      [self.view.layer addSublayer:shapeLayer];
      

    Xcode'un önceki sürümlerinde, QuartzCore.framework'ü projenizin "Link Binary with Libraries" e manuel olarak eklemeniz ve <QuartzCore/QuartzCore.h>başlığı .m dosyanıza aktarmanız gerekiyordu, ancak artık gerekli değil ("Modülleri Etkinleştir" ve "Bağlantı Çerçeveler Otomatik olarak "derleme ayarları açılır).

  2. Diğer yaklaşım, alt sınıflamak UIViewve ardından yöntemde CoreGraphics çağrılarını kullanmaktır drawRect:

    • Bir UIViewalt sınıf oluşturun ve drawRectçizginizi çizen bir alt sınıf tanımlayın .

      Bunu Core Graphics ile yapabilirsiniz:

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

      Veya kullanarak 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];
      }
      
    • Ardından, bu görünüm sınıfını NIB / film şeridiniz veya görünümünüz için temel sınıf olarak kullanabilir veya görünüm denetleyicinizin bunu bir alt görünüm olarak programlı bir şekilde eklemesini sağlayabilirsiniz:

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

Yukarıdaki iki yaklaşımın Swift yorumları aşağıdaki gibidir:

  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 alt sınıf:

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

    Ve bunu görünüm hiyerarşinize ekleyin:

    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
    

    Yukarıda, PathViewprogramlı olarak ekliyorum , ancak bunu IB aracılığıyla da ekleyebilir ve pathprogramlı olarak ayarlayabilirsiniz .


@Rob: Bu gayet iyi çalışıyor. Sizden AB'den bir çizgi mesafesi ve AB'den bir açı olduğunu sormak istiyorum, o zaman bu durumda başlangıç ​​ve bitiş noktalarını nasıl elde edebilirim? Lütfen bana yardım et.
Manthan

görüşüme uigesture ekledim ve yukarıdaki yoruma düz çizgi düzgün çalıştığını eklersem dokunma başlangıç ​​noktası ve hareket noktam var. ama başlangıç ​​noktasından yan tarafa dokunduğumda rengi işgal ediyor @Rob
Kishore Kumar

CAShapeLayer yöntemini kullanırken, çizgilerin bağlandıkça pürüzsüz görünmesi için çizgi uçlarını yuvarlatılmış olarak ayarlayabilirsiniz (ve muhtemelen isteyeceksiniz),shapeLayer.lineCap = kCALineCapRound;
Albert Renshaw

Veya, bağlandıkça pürüzsüz görünmesini istediğiniz birden çok hattınız varsa UIBezierPath, tekrarlanan addLineToPoint/ addLine(to:)çağrıları olan bir tek hattınız da olabilir . Daha sonra shapeLayer.lineJoin = kCALineJoinRoundya kCALineJoinBevelda ya da tercihinizi seçebilirsiniz kCALineJoinMiter.
Rob

Kod parçacığı için teşekkürler, bana oldukça zaman kazandırdı. Dinamik bir görünüm çizmeyi planlıyorsanız, bazı olaylara tepki verirseniz, alt katmanı çizimin başında şununla temizlemeniz gerekir: self.layer.sublayers = nil
Vilmir

15

Bir UIView oluşturun ve bunu görünüm denetleyicinizin görünümünün bir alt görünümü olarak ekleyin. Bu alt görünümün yüksekliğini veya genişliğini, bir çizgi gibi görünecek şekilde çok küçük olacak şekilde değiştirebilirsiniz. Çapraz bir çizgi çizmeniz gerekirse, alt görünümleri dönüştürme özelliğini değiştirebilirsiniz.

örneğin siyah yatay çizgi çizin. Bu, görünüm denetleyicinizin uygulamasından çağrılır

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

Bir ayırıcı öğe için (örneğin, UI öğeleri arasındaki bir çizgi) bunun en az direnç yolu olduğunu kabul etme eğilimindeyim. Bir CoreAnimation katmanını desteklemek için bir bitmap'e dönüştürmenin veya özel bir görünümde bir çizim işleyicisinin uygulanmasının alternatifi çok daha karmaşıktır. Tek bir satır olması koşuluyla, muhtemelen bu donanım da daha iyi hızlanır.
marko

4
Bunun neden berbat bir fikir olduğunun gerekçesini merak ediyorum. Bu çözümün kendi kodumda neden olabileceği herhangi bir sorundan kaçınmak istiyorum.
Jeff Ames

@sangony Bu tekniği de kodumda kullanıyorum. Ayrıca bunun neden korkunç bir fikir olduğunu da merak ediyorum. Lütfen açıklamaya yardım edin.
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)

8

İşte yararlı bulabileceğiniz harika bir teknik: Objective-C'de alt sınıflardan kaçınmak için çizim için blokları kullanma

Makalenin genel amaçlı görünüm alt sınıfını projenize dahil edin, ardından bu, anında bir çizgi çizen bir görünüm oluşturmak için görünüm denetleyicinize koyabileceğiniz kod türüdür:

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

Çizgi çizmek için UIImageView'ı kullanabilirsiniz.

Bununla birlikte, alt sınıflandırma atlamaya izin verir. Ve Core Graphics'e biraz eğilimli olduğum için hala kullanabilirim. Sadece koyabilirsiniz -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'un cevabına ek olarak, Bir çabukluk için, üçüncü yaklaşım bir UIImageView- onu örtmek - xib görünümünü kullanmaktır. (Bu, xcode 5'te xib'e sürüklendiğinde varsayılan UIImageView görünümüdür)

Şerefe ve +1!


2

Gerçekten yapmamalısınız, ancak herhangi bir nedenden dolayı size mantıklı geliyorsa, DelegateDrawViewörneğin, aşağıdaki gibi bir yöntemi uygulayan bir temsilciyi alan bir UIView alt sınıfı oluşturabilirsiniz.

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

ve sonra yöntemlerde - [DelegateDrawView drawRect:]temsilci yönteminizi çağırmalısınız.

Ama neden denetleyicinize görüntüleme kodunu yerleştirmek istersiniz?

İki köşesi arasına bir çizgi çizen bir UIView alt sınıfı oluşturmakta daha iyi olursunuz, hangi ikisini ayarlamak için bir özelliğe sahip olabilir ve ardından görünümü istediğiniz yere konumlandırabilirsiniz.


1

Görüşünüzün içini çizmek çok basit, @ ROB ilk yöntemi benim aldığım 2 yöntemi söyledi.

Kodu kopyalayıp istediğiniz yere yapıştırı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);
}

Parmağın hareket ettiği bir çizgi gibi çizecek

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.