Cocoa Touch: Jak zmienić kolor i grubość obramowania UIView?

Odpowiedzi:

596

Aby ustawić właściwość obramowania, musisz użyć warstwy widoku. na przykład:

#import <QuartzCore/QuartzCore.h>
...
view.layer.borderColor = [UIColor redColor].CGColor;
view.layer.borderWidth = 3.0f;

Aby uzyskać dostęp do tej funkcji, musisz także połączyć się z QuartzCore.framework.

Vladimir
źródło
Czy istnieje sposób ustawienia różnych szerokości / kolorów dla każdej strony?
lluismontero
2
@lluismontero Obawiam się, że będziesz musiał zrobić niestandardowy UIView z drawRect: metoda do tego
Vladimir
1
Jeśli to zrobię i oprócz tego widoku mam animację, na przykład odrzucenie modalnego modułu UINavigationController, widzę wiele błędów, które trudno opisać. Jeśli dezaktywuję granice, wszystko wraca do normy.
Nicu Surdu,
5
Tylko heads up dla innych. Xcode sugeruje zmostkowanie UIColor do CGColorRef as __bridge CGColorRef. To nie działa. Musi być [UIColor blackColor].CGColortak, jak wspomniano w odpowiedzi.
GoodSp33d,
6
#import <QuartzCore/QuartzCore.h>nie jest już potrzebny.
znaczenie-ważne
43

Aktualizacja Xcode 6

Od najnowszej wersji Xcode istnieje lepsze rozwiązanie tego problemu:

Z @IBInspectablemożna ustawić atrybuty bezpośrednio od wewnątrzAttributes Inspector .

Mój widok niestandardowy @IBInspectable Attributes

To ustawia User Defined Runtime Attributesdla ciebie:

wprowadź opis zdjęcia tutaj

Istnieją dwa podejścia do skonfigurowania tego:

Opcja 1 (z aktualizacją na żywo w Storyboard)

  1. Utwórz MyCustomView.
  2. To dziedziczy po UIView.
  3. Ustaw @IBDesignable(powoduje to, że aktualizacja Wyświetl na żywo). *
  4. Ustaw atrybuty środowiska wykonawczego (obramowanie itp.) Za pomocą @IBInspectable
  5. Zmień klasę wyświetleń na MyCustomView
  6. Edytuj w panelu atrybutów i zobacz zmiany w Storyboard :)

`

@IBDesignable
class MyCustomView: UIView {
    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
            layer.masksToBounds = cornerRadius > 0
        }
    }
    @IBInspectable var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    @IBInspectable var borderColor: UIColor? {
        didSet {
            layer.borderColor = borderColor?.CGColor
        }
    }
}

* @IBDesignabledziała tylko, gdy jest ustawiony na początkuclass MyCustomView

Opcja 2 (nie działa od Swift 1.2, patrz komentarze)

Rozszerz swoją klasę UIView:

extension UIView {
    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
            layer.masksToBounds = cornerRadius > 0
        }
    }
    @IBInspectable var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    @IBInspectable var borderColor: UIColor? {
        didSet {
            layer.borderColor = borderColor?.CGColor
        }
    }
}

W ten sposób domyślny widok zawsze zawiera te dodatkowe pola edytowalne Attributes Inspector. Kolejną zaletą jest to, że nie musisz za MycustomViewkażdym razem zmieniać klasy . Wadą tego jest jednak to, że zmiany będą widoczne dopiero po uruchomieniu aplikacji.

MMachinegun
źródło
1
Twoje rozszerzenie nie działa w najnowszej wersji Xcode na dzień dzisiejszy.
Edgar Aroutiounian
1
Potwierdzę @EdgarAroutiounian, żeby tylko powiedzieć, że w Swift 1.2 drugie podejście już nie działa
Sergey Grischyov
Co z celem C?
Nik Kov
Sprawdź odpowiedź spencery2 poniżej, poświęcił czas na napisanie jej w Celu C:
MMachinegun
zaktualizuj rozszerzenie, aby nie zapisywało wartości i użyj set (wartość) {} i uzyskaj {}, aby uzyskać bezpośredni dostęp do warstwy, to wtedy działa.
LightningStryk,
17

Możesz także utworzyć obramowanie z kolorem swojego życzenia.

view.layer.borderColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0].CGColor;

* r, g, b są wartościami od 0 do 255.

rohan-patel
źródło
6
Warto zauważyć, że r, gi bpowinny prawdopodobnie pływaków; z dzielnikami powinny być również pływaki: r / 255.0.
nie, składnia C nie jest taka, jak mówisz. r, gib mogą być int, ponieważ promocje C (i objC IS C) rzucą int r = 128 na podwojone, gdy: r / 255.0. Nawiasem mówiąc, otrzymałeś podwójnie, a Clang rzuci na CGFloat.
ingconti
10

Dodaj następujące @IBInspectables w rozszerzeniu UIView

extension UIView {

  @IBInspectable var borderWidth: CGFloat {
    get {
      return layer.borderWidth
    }
    set(newValue) {
      layer.borderWidth = newValue
    }
  }

  @IBInspectable var borderColor: UIColor? {
    get {
      if let color = layer.borderColor {
        return UIColor(CGColor: color)
      }
      return nil
    }
    set(newValue) {
      layer.borderColor = newValue?.CGColor
    }
  }
}

A potem powinieneś być w stanie ustawić atrybuty borderColor i borderWidth bezpośrednio z Inspektora atrybutów. Zobacz załączony obraz

Inspektor atrybutów

Bikramjit Singh
źródło
8

Kiedy korzystam z rozwiązania CALayer Vladimira, a na wierzchu mam animację, taką jak modalne odrzucanie UINavigationController, widzę wiele błędów i problemy z wydajnością rysowania.

Tak więc innym sposobem na osiągnięcie tego, ale bez błędów i utraty wydajności, jest utworzenie niestandardowego UIView i zaimplementowanie drawRectkomunikatu w następujący sposób:

- (void)drawRect:(CGRect)rect
{
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(contextRef, 1);
    CGContextSetRGBStrokeColor(contextRef, 255.0, 255.0, 255.0, 1.0);
    CGContextStrokeRect(contextRef, rect);    
}
Nicu Surdu
źródło
@Krish Przechowujesz kolor w zmiennej, a kiedy musisz zmienić kolor obramowania, zmieniasz wartość zmiennej i wywołujesz setNeedsDisplaywidok. Wymusi to przerysowanie UIView i pośrednio nastąpi ponowne wywołanie drawRect. Nie testowane, choć ...
Nicu Surdu,
8
view.layer.borderWidth = 1.0
view.layer.borderColor = UIColor.lightGray.cgColor
Shaiju Mathew
źródło
1
Naprawdę nie lubię głosowania ludzi bez wyjaśnienia. To nikomu nie pomaga.
David DelMonte
7

Wypróbuj ten kod:

view.layer.borderColor =  [UIColor redColor].CGColor;
view.layer.borderWidth= 2.0;
[view setClipsToBounds:YES];
Vikram Biwal
źródło
4

Nie sugerowałbym przesłonięcia metody drawRect z powodu spowodowania spadku wydajności.

Zamiast tego zmodyfikowałbym właściwości klasy jak poniżej (w niestandardowym uiview):

  - (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
      self.layer.borderWidth = 2.f;
      self.layer.borderColor = [UIColor redColor].CGColor;
    }
  return self;

Nie widziałem żadnych błędów przy powyższym podejściu - nie jestem pewien, dlaczego włożenie initWithFrame zatrzymuje je ;-)

DEzra
źródło
3

Chciałem dodać to do odpowiedzi @ marczking ( Opcja 1 ) jako komentarz, ale mój niski status na StackOverflow temu zapobiega.

Zrobiłem port odpowiedzi @ marczking na Cel C. Działa jak urok, dzięki @marczking!

UIView + Border.h:

#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface UIView (Border)

-(void)setBorderColor:(UIColor *)color;
-(void)setBorderWidth:(CGFloat)width;
-(void)setCornerRadius:(CGFloat)radius;

@end

UIView + Border.m:

#import "UIView+Border.h"

@implementation UIView (Border)
// Note: cannot use synthesize in a Category

-(void)setBorderColor:(UIColor *)color
{
    self.layer.borderColor = color.CGColor;
}

-(void)setBorderWidth:(CGFloat)width
{
    self.layer.borderWidth = width;
}

-(void)setCornerRadius:(CGFloat)radius
{
    self.layer.cornerRadius = radius;
    self.layer.masksToBounds = radius > 0;
}

@end
spencery2
źródło
2

@IBInspectable działa dla mnie na iOS 9, Swift 2.0

extension UIView {

@IBInspectable var borderWidth: CGFloat {
get {
        return layer.borderWidth
    }
    set(newValue) {
        layer.borderWidth = newValue
    }
}

@IBInspectable var cornerRadius: CGFloat {
    get {
        return layer.cornerRadius
    }
    set(newValue) {
        layer.cornerRadius = newValue
    }
}

@IBInspectable var borderColor: UIColor? {
    get {
        if let color = layer.borderColor {
            return UIColor(CGColor: color)
        }
        return nil
    }
    set(newValue) {
        layer.borderColor = newValue?.CGColor
    }
}
anhtran
źródło
1

Jeśli nie chcesz edytować warstwy UIView, zawsze możesz osadzić widok w innym widoku. Widok nadrzędny miałby kolor tła ustawiony na kolor obramowania. Byłby również nieco większy, w zależności od tego, jak szeroka ma być granica.

Oczywiście działa to tylko wtedy, gdy twój widok nie jest przezroczysty i potrzebujesz tylko jednego koloru obramowania. OP chciał samej granicy w samym widoku, ale może to być realna alternatywa.

Matt Becker
źródło
0

Jeśli chcesz dodać inną ramkę z różnych stron, możesz dodać widok podrzędny z konkretnym stylem, jest to sposób łatwy do wymyślenia.

Yuanfei Zhu
źródło
0

kolor obramowania przedmiotu w szybkim 4.2:

let cell = tableView.dequeueReusableCell(withIdentifier: "Cell_lastOrderId") as! Cell_lastOrder
cell.layer.borderWidth = 1
cell.layer.borderColor = UIColor.white.cgColor
cell.layer.cornerRadius = 10
Akhrua
źródło