Desenhar linha em UIView

86

Eu preciso desenhar uma linha horizontal em um UIView. Qual é a maneira mais fácil de fazer isso. Por exemplo, eu quero desenhar uma linha horizontal preta em y-coord = 200.

NÃO estou usando o Interface Builder.

John smith
fonte
1
Esta é a solução completa e fácil para ter corretamente uma linha de pixel único, em todos os iOS, e facilitar no storyboard: stackoverflow.com/a/26525466/294884
Fattie

Respostas:

122

A maneira mais fácil no seu caso (linha horizontal) é adicionar uma subvisualização com fundo preto e moldura [0, 200, 320, 1].

Amostra de código (espero que não haja erros - escrevi sem o Xcode):

UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 200, self.view.bounds.size.width, 1)];
lineView.backgroundColor = [UIColor blackColor];
[self.view addSubview:lineView];
[lineView release];
// You might also keep a reference to this view 
// if you are about to change its coordinates.
// Just create a member and a property for this...

Outra maneira é criar uma classe que desenhará uma linha em seu método drawRect (você pode ver meu exemplo de código para isso aqui ).

Michael Kessler
fonte
Faça isso sem nenhum código no Storyboard. É mais fácil e melhor.
coolcool1994
3
@ coolcool1994, você não percebeu "NÃO estou usando o Interface Builder." requisito na questão?
Michael Kessler
312

Talvez seja um pouco tarde, mas quero acrescentar que existe uma maneira melhor. Usar o UIView é simples, mas relativamente lento. Este método substitui a forma como a visualização se desenha e é mais rápido:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);

    // Draw them with a 2.0 stroke width so they are a bit more visible.
    CGContextSetLineWidth(context, 2.0f);

    CGContextMoveToPoint(context, 0.0f, 0.0f); //start at this point

    CGContextAddLineToPoint(context, 20.0f, 20.0f); //draw to this point

    // and now draw the Path!
    CGContextStrokePath(context);
}
b123400
fonte
Se este código for usado em um UIView, as coordenadas neste exemplo são relativas aos limites da visualização ou da tela inteira?
ChrisP de
Não é preciso ligar CGContextBeginPath(context);antes CGContextMoveToPoint(...);?
i_am_jorf
37
Usar uma visão para fazer isso não é tão horrível. Isso torna as coisas mais fáceis, por exemplo, ao fazer animações implícitas durante mudanças de orientação. Se for apenas um, outro UIView não é tão caro e o código é muito mais simples.
i_am_jorf
Além disso, você pode obter limites e pontos médios com estes códigos: CGPoint midPoint; midPoint.x = self.bounds.size.width / 2; midPoint.y = self.bounds.size.height / 2;
coolcool1994,
1
Certo, o comentário de que é "lento" não faz sentido. Há um grande número de UIViews simples na tela a qualquer momento. Observe que desenhar UM (!) Caractere de texto, com todo o processamento que envolve, atrapalha o desenho de um UIView preenchido.
Fattie
29

Swift 3 e Swift 4

É assim que você pode desenhar uma linha cinza no final de sua visualização (mesma ideia da resposta de b123400)

class CustomView: UIView {

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        if let context = UIGraphicsGetCurrentContext() {
            context.setStrokeColor(UIColor.gray.cgColor)
            context.setLineWidth(1)
            context.move(to: CGPoint(x: 0, y: bounds.height))
            context.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
            context.strokePath()
        }
    }
}
Guy Daher
fonte
O "contexto if let" falha quando chamado de viewDidLayoutSubviews.
Oscar
14

Basta adicionar um rótulo sem texto e com a cor de fundo. Defina as coordenadas de sua escolha e também a altura e largura. Você pode fazer isso manualmente ou com o Interface Builder.

Phanindra
fonte
11

Você pode usar a classe UIBezierPath para isso:

E pode desenhar quantas linhas você quiser:

Eu criei uma subclasse de UIView:

    @interface MyLineDrawingView()
    {
       NSMutableArray *pathArray;
       NSMutableDictionary *dict_path;
       CGPoint startPoint, endPoint;
    }

       @property (nonatomic,retain)   UIBezierPath *myPath;
    @end

E inicializou os objetos pathArray e dictPAth que serão usados ​​para desenho de linha. Estou escrevendo a parte principal do código do meu próprio projeto:

- (void)drawRect:(CGRect)rect
{

    for(NSDictionary *_pathDict in pathArray)
    {
        [((UIColor *)[_pathDict valueForKey:@"color"]) setStroke]; // this method will choose the color from the receiver color object (in this case this object is :strokeColor)
        [[_pathDict valueForKey:@"path"] strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
    }

    [[dict_path objectForKey:@"color"] setStroke]; // this method will choose the color from the receiver color object (in this case this object is :strokeColor)
    [[dict_path objectForKey:@"path"] strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];

}

Método touchesBegin:

UITouch *touch = [touches anyObject];
startPoint = [touch locationInView:self];
myPath=[[UIBezierPath alloc]init];
myPath.lineWidth = currentSliderValue*2;
dict_path = [[NSMutableDictionary alloc] init];

Método touchesMoved:

UITouch *touch = [touches anyObject];
endPoint = [touch locationInView:self];

 [myPath removeAllPoints];
        [dict_path removeAllObjects];// remove prev object in dict (this dict is used for current drawing, All past drawings are managed by pathArry)

    // actual drawing
    [myPath moveToPoint:startPoint];
    [myPath addLineToPoint:endPoint];

    [dict_path setValue:myPath forKey:@"path"];
    [dict_path setValue:strokeColor forKey:@"color"];

    //                NSDictionary *tempDict = [NSDictionary dictionaryWithDictionary:dict_path];
    //                [pathArray addObject:tempDict];
    //                [dict_path removeAllObjects];
    [self setNeedsDisplay];

Método touchesEnded:

        NSDictionary *tempDict = [NSDictionary dictionaryWithDictionary:dict_path];
        [pathArray addObject:tempDict];
        [dict_path removeAllObjects];
        [self setNeedsDisplay];
Rakesh
fonte
11

Uma outra possibilidade (e ainda mais curta). Se você estiver dentro de drawRect, algo como o seguinte:

[[UIColor blackColor] setFill];
UIRectFill((CGRect){0,200,rect.size.width,1});
hkatz
fonte
0

Baseado na resposta de Guy Daher.

Tento evitar usar? porque pode causar um travamento do aplicativo se GetCurrentContext () retornar nulo.

Eu não faria nada verificar se a declaração:

class CustomView: UIView 
{    
    override func draw(_ rect: CGRect) 
    {
        super.draw(rect)
        if let context = UIGraphicsGetCurrentContext()
        {
            context.setStrokeColor(UIColor.gray.cgColor)
            context.setLineWidth(1)
            context.move(to: CGPoint(x: 0, y: bounds.height))
            context.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
            context.strokePath()
        }
    }
}
Robert Harrold
fonte
2
se o motivo da ifcláusula for apenas desencaixotar do opcional, prefira usar uma guardinstrução.
Julio Flores
-1

Adicionar rótulo sem texto e com a cor de fundo do tamanho do quadro correspondente (ex: altura = 1). Faça isso por meio de código ou no construtor de interface.

Reddy
fonte