Jak narysować niestandardowy UIView, który jest tylko okręgiem - aplikacja na iPhone'a

129

Jak mam się zabrać za narysowanie niestandardowego UIView, który jest dosłownie tylko piłką (okrąg 2D)? Czy po prostu zastąpiłbym metodę drawRect? Czy ktoś może mi pokazać kod do rysowania niebieskiego koła?

Czy byłoby w porządku, aby zmienić ramy tego widoku w samej klasie? A może muszę zmienić ramkę z innej klasy?

(tylko próbuję ustawić piłkę odbijającą się dookoła)

StanLe
źródło

Odpowiedzi:

211

Możesz użyć QuartzCore i zrobić coś takiego -

self.circleView = [[UIView alloc] initWithFrame:CGRectMake(10,20,100,100)];
self.circleView.alpha = 0.5;
self.circleView.layer.cornerRadius = 50;  // half the width/height
self.circleView.backgroundColor = [UIColor blueColor];
Kal
źródło
104
FYI, aby Twój widok był okręgiem przy użyciu QuartCore, promień narożnika musi wynosić połowę wysokości / szerokości ramy. Jest to najłatwiejszy sposób na zrobienie okręgu, ale niekoniecznie najbardziej efektywny. Jeśli wydajność jest kluczowa, drawRect prawdopodobnie da lepsze wyniki.
Benjamin Mayo,
4
I to jest. 100 to wysokość / szerokość.
Kal,
3
Tak, przepraszam, że nie kierowałem tego do ciebie, Kal. Wspominałem o tym StanLe na wypadek, gdyby chciał użyć widoków o różnych rozmiarach.
Benjamin Mayo
Jeśli twój circleView rozmiar nie jest 100X100, cornerRadius powinien być (nowy rozmiar) / 2
gran33
Ale zauważyłem, że okrąg z promieniem narożnika ma czasami lekko „płaskie” / obcięte krawędzie. Przynajmniej gdy jest używany z obramowaniem. Każdy pomysł, dlaczego? Promień jest dokładnie o połowę mniejszy od rozmiaru widoku. Myślałem, że może to być problem z przycinaniem, ale nie wygląda na to, próbowałem nawet z mniejszą warstwą dolną - nadal mają ten sam efekt.
Ixx
135

Czy po prostu zastąpiłbym metodę drawRect?

Tak:

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextAddEllipseInRect(ctx, rect);
    CGContextSetFillColor(ctx, CGColorGetComponents([[UIColor blueColor] CGColor]));
    CGContextFillPath(ctx);
}

Czy byłoby w porządku, aby zmienić ramy tego widoku w samej klasie?

Idealnie nie, ale możesz.

A może muszę zmienić ramkę z innej klasy?

Pozwoliłbym rodzicowi to kontrolować.

Steve
źródło
1
Jak mogę ustawić niestandardowe kolory, nie tylko niebieski? Próbuję użyć koloru białego i niebieskiego w ten sposób: ([self.colorOfCircle CGColor]), ale nic się nie dzieje: \
gaussblurinc
@loldop to self.colorOfCircle a UIColor? Czy to jest ustawione? Jeśli umieścisz punkt przerwania w tej linii i spojrzysz na wartość, czy tego się spodziewasz? JEŚLI ustawisz to po fakcie, będziesz musiał unieważnić część widoku zawierającą okrąg.
Steve,
9
@gaussblurinc CGContextSetFillColorWithColor(ctx, self.colorOfCircle.CGColor);, metoda zaproponowana w rozwiązaniu CGColorGetComponentsdziała tylko z niektórymi kolorami, patrz stackoverflow.com/questions/9238743/…
yonilevy
Uwaga dla każdego, kto utknie na tym. Zamiast recttego przypadkowo użyłem self.frameelipsy. Prawidłowa wartość to self.bounds. D'oh! :)
Olie
31

Oto inny sposób za pomocą UIBezierPath (może jest już za późno ^^) Utwórz okrąg i maskuj nim UIView w następujący sposób:

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
view.backgroundColor = [UIColor blueColor];

CAShapeLayer *shape = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:view.center radius:(view.bounds.size.width / 2) startAngle:0 endAngle:(2 * M_PI) clockwise:YES];
shape.path = path.CGPath;
view.layer.mask = shape;
Gintama
źródło
1
Naprawdę nie ma potrzeby, aby ta warstwa kształtu była maską; możesz po prostu użyć warstwy kształtu bezpośrednio jako warstwy widoku i nadać jej kolor wypełnienia. Maska jest prawdopodobnie znacznie droższa.
Jesse Rusak
Warstwa UIView to CALayer, więc jak możemy narysować kształt na tej warstwie. Przypuszczam, że dodamy warstwę kształtu do warstwy widoku bez jej maskowania?
Gintama,
1
Możesz podklasę UIView i przesłonić layerClassmetodę klasy, aby uczynić ją warstwą kształtu.
Jesse Rusak
@JesseRusak, problem, który mam z twoją sugestią (aby ustawić kształt na samej warstwie UIView) polega na tym, że nie możesz poprawnie użyć backgroundColor. Należy zastosować fillColor i ustawić backgroundColor na clearColor, co oznacza, że ​​użytkownik UIView nie byłby w stanie naturalnie ustawić backgroundColor.
Richard Venable,
26

Mój wkład w rozszerzenie Swift:

extension UIView {
    func asCircle() {
        self.layer.cornerRadius = self.frame.width / 2;
        self.layer.masksToBounds = true
    }
}

Zadzwoń myView.asCircle()

Kevin ABRIOUX
źródło
Ustawienie masksToBoundsna wartość true i używanie selfsą opcjonalne w tej odpowiedzi, ale nadal jest najkrótsza i najlepszym rozwiązaniem
Max Desiatov
17

Swift 3 - klasa niestandardowa, łatwa do ponownego użycia. Używa backgroundColorzestawu w konstruktorze UI

import UIKit

@IBDesignable
class CircleBackgroundView: UIView {

    override func layoutSubviews() {
        super.layoutSubviews()
        layer.cornerRadius = bounds.size.width / 2
        layer.masksToBounds = true
    }

}
Maciej
źródło
1
Świetny. To jest poprawne, adaptacyjne podejście.
Womble
14

Klasa Swift 3 :

import UIKit

class CircleView: UIView {

    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else {return}

        context.addEllipse(in: rect)
        context.setFillColor(.blue.cgColor)
        context.fillPath()
    }           
}
Wiaczesław
źródło
6

Innym sposobem podejścia do rysowania koła (i innych kształtów) jest użycie masek. Rysujesz koła lub inne kształty, po pierwsze, tworząc maski o potrzebnych kształtach, po drugie, dostarczając kwadraty swojego koloru, a po trzecie, nakładając maski na te kwadraty koloru. Możesz zmienić maskę lub kolor, aby uzyskać nowy niestandardowy okrąg lub inny kształt.

#import <QuartzCore/QuartzCore.h>

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *area1;
@property (weak, nonatomic) IBOutlet UIView *area2;
@property (weak, nonatomic) IBOutlet UIView *area3;
@property (weak, nonatomic) IBOutlet UIView *area4;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.area1.backgroundColor = [UIColor blueColor];
    [self useMaskFor: self.area1];

    self.area2.backgroundColor = [UIColor orangeColor];
    [self useMaskFor: self.area2];

    self.area3.backgroundColor = [UIColor colorWithRed: 1.0 green: 0.0 blue: 0.5 alpha:1.0];
    [self useMaskFor: self.area3];

    self.area4.backgroundColor = [UIColor colorWithRed: 1.0 green: 0.0 blue: 0.5 alpha:0.5];
    [self useMaskFor: self.area4];        
}

- (void)useMaskFor: (UIView *)colorArea {        
    CALayer *maskLayer = [CALayer layer];
    maskLayer.frame = colorArea.bounds;
    UIImage *maskImage = [UIImage imageNamed:@"cirMask.png"];
    maskLayer.contents = (__bridge id)maskImage.CGImage;
    colorArea.layer.mask = maskLayer;
}

@end

Oto wynik powyższego kodu:

cztery okręgi

matrix3003
źródło
2

Jest inna alternatywa dla leniwych ludzi. Możesz ustawić layer.cornerRadius ścieżkę klucza dla swojego widoku w programie Interface Builder . Na przykład, jeśli Twój widok ma szerokość = wysokość 48 , ustaw layer.cornerRadius = 24:

wprowadź opis obrazu tutaj

Jednak działa to tylko wtedy, gdy masz statyczny rozmiar widoku (width/height is fixed)i nie wyświetla koła w konstruktorze interfejsu.

user8675
źródło
1

Swift 3 - Xcode 8.1.0

@IBOutlet weak var myView: UIView!

override func viewDidLoad() {
    super.viewDidLoad() 

    let size:CGFloat = 35.0
    myView.bounds = CGRect(x: 0, y: 0, width: size, height: size)
    myView.layer.cornerRadius = size / 2
    myView.layer.borderWidth = 1
    myView.layer.borderColor = UIColor.Gray.cgColor        
}
Mohammad Razipour
źródło