Zaokrąglone UIView za pomocą CALayers - tylko niektóre rogi-jak?

W mojej aplikacji-są cztery przyciski o następującej nazwie:

  • górny-lewy
  • dolny-lewy
  • górny - prawy
  • dolny - prawy

Nad przyciskami znajduje się widok obrazu (lub UIView).

Załóżmy, że użytkownik dotknie przycisku-górny-lewy. Powyższy obraz / widok powinien być zaokrąglony w tym konkretnym rogu.

Mam pewne trudności z nałożeniem zaokrąglonych rogów na UIView.

W tej chwili używam poniższego kodu aby zastosować zaokrąglone rogi do każdego widoku:

    // imgVUserImg is a image view on IB.
    imgVUserImg.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"any Url Here"];
    CALayer *l = [imgVUserImg layer];
    [l setMasksToBounds:YES];
    [l setCornerRadius:5.0];  
    [l setBorderWidth:2.0];
    [l setBorderColor:[[UIColor darkGrayColor] CGColor]];

Powyższy kod stosuje zaokrąglenie do każdego z rogów dostarczonego widoku. Zamiast tego chciałem zastosować zaokrąglenie do wybranych rogów, takich jak-Góra / Góra+Lewo / Dół+prawo itp.

Czy to możliwe? Jak?
Author: WMios, 2010-02-15

13 answers

Użyłem odpowiedzi na Jak stworzyć okrągły UILabel na iPhonie? A kod z Jak robi się zaokrąglony widok rect z przezroczystością na iPhonie? aby stworzyć ten kod.

Potem zdałem sobie sprawę, że odpowiedziałem na złe pytanie (dałem zaokrąglony UILabel zamiast UIImage), więc użyłem tego kodu, aby go zmienić:

Http://discussions.apple.com/thread.jspa?threadID=1683876

Stwórz projekt iPhone ' a za pomocą szablonu widoku. W widoku controller, add this:

- (void)viewDidLoad
{
    CGRect rect = CGRectMake(10, 10, 200, 100);
    MyView *myView = [[MyView alloc] initWithFrame:rect];
    [self.view addSubview:myView];
    [super viewDidLoad];
}

MyView jest tylko UIImageView podklasa:

@interface MyView : UIImageView
{
}

Nigdy wcześniej nie używałem kontekstów graficznych, ale udało mi się połączyć ten kod. Brakuje kodu do dwóch narożników. Jeśli czytasz Kod, możesz zobaczyć, jak to zaimplementowałem (usuwając niektóre wywołania CGContextAddArc i usuwając niektóre wartości radius w kodzie. Kod dla wszystkich narożników jest tam, więc użyj go jako punktu wyjścia i usuń części, które tworzą narożniki, których nie potrzebujesz. Uwaga możesz też tworzyć prostokąty z 2 lub 3 zaokrąglonymi rogami, jeśli chcesz.

Kod nie jest idealny, ale jestem pewien, że możesz go trochę uporządkować.
static void addRoundedRectToPath(CGContextRef context, CGRect rect, float radius, int roundedCornerPosition)
{

    // all corners rounded
    //  CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
    //  CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
    //  CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, 
    //                  radius, M_PI / 4, M_PI / 2, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius, 
    //                          rect.origin.y + rect.size.height);
    //  CGContextAddArc(context, rect.origin.x + rect.size.width - radius, 
    //                  rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
    //  CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, 
    //                  radius, 0.0f, -M_PI / 2, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
    //  CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, 
    //                  -M_PI / 2, M_PI, 1);

    // top left
    if (roundedCornerPosition == 1) {
        CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
        CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, 
                        radius, M_PI / 4, M_PI / 2, 1);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, 
                                rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y);
    }   

    // bottom left
    if (roundedCornerPosition == 2) {
        CGContextMoveToPoint(context, rect.origin.x, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, 
                                rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
        CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, 
                        -M_PI / 2, M_PI, 1);
    }

    // add the other corners here


    CGContextClosePath(context);
    CGContextRestoreGState(context);
}


-(UIImage *)setImage
{
    UIImage *img = [UIImage imageNamed:@"my_image.png"];
    int w = img.size.width;
    int h = img.size.height;

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);

    CGContextBeginPath(context);
    CGRect rect = CGRectMake(0, 0, w, h);


    addRoundedRectToPath(context, rect, 50, 1);
    CGContextClosePath(context);
    CGContextClip(context);

    CGContextDrawImage(context, rect, img.CGImage);

    CGImageRef imageMasked = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    [img release];

    return [UIImage imageWithCGImage:imageMasked];
}

Alt text http://nevan.net/skitch/skitched-20100224-092237.png

Nie zapominaj, że musisz umieścić tam Framework QuartzCore, aby to zadziałało.

 43
Author: nevan king,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:37

Począwszy od systemu iOS 3.2, możesz użyć funkcji UIBezierPaths, aby utworzyć Zaokrąglony prostokąt (gdzie tylko określone rogi są zaokrąglone). Następnie można użyć tego jako ścieżki CAShapeLayer i użyć tego jako maski dla warstwy widoku:

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds 
                                               byRoundingCorners:UIRectCornerTopLeft
                                                     cornerRadii:CGSizeMake(10.0, 10.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageView.bounds;
maskLayer.path = maskPath.CGPath;

// Set the newly created shape layer as the mask for the image view's layer
imageView.layer.mask = maskLayer;

I tyle - bez bałaganu ręcznego definiowania kształtów w grafice Core, bez tworzenia maskowania obrazów w Photoshopie. Warstwa nie wymaga nawet unieważniania. Zastosowanie zaokrąglonego narożnika lub zmiana na nowy jest równie proste jako definiowanie nowej UIBezierPath i używanie jej CGPath jako ścieżki warstwy maski. Parametr corners metody bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: jest maską bitową, więc wiele narożników może być zaokrąglonych przez ORing ich razem.


EDIT: dodawanie cienia

Jeśli chcesz dodać do tego Cień, potrzeba trochę więcej pracy.

Ponieważ "imageView.layer.mask = maskLayer" stosuje maskę, Cień zwykle nie będzie się pojawiał poza nią. Sztuczka polega na użyciu przezroczystego widoku, a następnie dodaniu dwóch podwarstw (CALayer s) do warstwy widoku: shadowLayer i roundedLayer. Oba muszą korzystać z UIBezierPath. Obraz jest dodawany jako zawartość roundedLayer.

// Create a transparent view
UIView *theView = [[UIView alloc] initWithFrame:theFrame];
[theView setBackgroundColor:[UIColor clearColor]];

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:theView.bounds 
                                               byRoundingCorners:UIRectCornerTopLeft
                                                     cornerRadii:CGSizeMake(10.0f, 10.0f)];

// Create the shadow layer
CAShapeLayer *shadowLayer = [CAShapeLayer layer];
[shadowLayer setFrame:theView.bounds];
[shadowLayer setMasksToBounds:NO];
[shadowLayer setShadowPath:maskPath.CGPath];
// ...
// Set the shadowColor, shadowOffset, shadowOpacity & shadowRadius as required
// ...

// Create the rounded layer, and mask it using the rounded mask layer
CALayer *roundedLayer = [CALayer layer];
[roundedLayer setFrame:theView.bounds];
[roundedLayer setContents:(id)theImage.CGImage];

CAShapeLayer *maskLayer = [CAShapeLayer layer];
[maskLayer setFrame:theView.bounds];
[maskLayer setPath:maskPath.CGPath];

roundedLayer.mask = maskLayer;

// Add these two layers as sublayers to the view
[theView.layer addSublayer:shadowLayer];
[theView.layer addSublayer:roundedLayer];
 354
Author: Stuart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-02 06:47:30

Użyłem tego kodu w wielu miejscach w moim kodzie i działa w 100% poprawnie. Możesz zmienić dowolny corder zmieniając jedną właściwość "Byroundingcorners: UIRectCornerBottomLeft"

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerBottomLeft cornerRadii:CGSizeMake(10.0, 10.0)];

                CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
                maskLayer.frame = view.bounds;
                maskLayer.path = maskPath.CGPath;
                view.layer.mask = maskLayer;
                [maskLayer release];
 18
Author: itsaboutcode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-20 18:33:05

Stuarts przykład zaokrąglania konkretnych narożników działa świetnie. Jeśli chcesz zaokrąglać wiele rogów, takich jak górny lewy i prawy, oto jak to zrobić

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageview
                                               byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight
                                                     cornerRadii:CGSizeMake(10.0, 10.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageview.bounds;
maskLayer.path = maskPath.CGPath;

// Set the newly created shape layer as the mask for the image view's layer
imageview.layer.mask = maskLayer; 
 5
Author: aZtraL-EnForceR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-11 12:43:45

Dzięki za podzielenie się. Tutaj chciałbym podzielić się rozwiązaniem na swift 2.0 w celu uzyskania dalszych informacji na ten temat. (aby dostosować protokół UIRectCorner)

let mp = UIBezierPath(roundedRect: cell.bounds, byRoundingCorners: [.bottomLeft, .TopLeft], cornerRadii: CGSize(width: 10, height: 10))
let ml = CAShapeLayer()
ml.frame = self.bounds
ml.path = mp.CGPath
self.layer.mask = ml
 5
Author: Jog Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-12 00:57:37

Istnieje łatwiejsza i szybsza odpowiedź, która może działać w zależności od twoich potrzeb, a także działa z cieniami. można ustawić maskobound na superlayerze na true i przesunąć warstwy potomne tak, aby 2 ich rogi znajdowały się poza granicami superlayera, skutecznie odcinając zaokrąglone rogi z dwóch stron.

Oczywiście działa to tylko wtedy, gdy chcemy mieć tylko 2 zaokrąglone rogi po tej samej stronie, a zawartość warstwy wygląda tak samo po odcięciu kilku pikseli z jednej strony. świetnie sprawdza się przy zaokrąglaniu wykresów słupkowych tylko na górze.

 4
Author: user1259710,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-04 11:34:20

Rozszerzenie Calayera z Swift 3 i wyżej składnia

extension CALayer {

    func round(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize) -> Void {
        let bp = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: cornerRadii)
        let sl = CAShapeLayer()
        sl.frame = self.bounds
        sl.path = bp.cgPath
        self.mask = sl
    }
}

Może być używany w następujący sposób:

let layer: CALayer = yourView.layer
layer.round(roundedRect: yourView.bounds, byRoundingCorners: [.bottomLeft, .topLeft], cornerRadii: CGSize(width: 5, height: 5))
 4
Author: Maverick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-09 07:40:38

W iOS 11 możemy teraz tylko zaokrąglać niektóre rogi

let view = UIView()

view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
 4
Author: onmyway133,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-02 09:50:27

Zobacz to powiązane pytanie . Musisz narysować własny prostokąt na CGPath z kilkoma zaokrąglonymi rogami, dodać CGPath do swojego CGContext, a następnie przyciąć do niego za pomocą CGContextClip.

Możesz również narysować zaokrąglony rect z wartościami alfa na obrazie, a następnie użyć go do utworzenia nowej warstwy, którą ustawisz jako właściwość warstwy mask ( zobacz dokumentację Apple).

 3
Author: MrMage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:37

Pół dekady później, ale myślę, że obecny sposób, w jaki ludzie to robią, nie jest w 100% właściwy. Wiele osób miało problem z tym, że używanie metody Uibezierpath + CAShapeLayer zakłóca automatyczny układ, zwłaszcza gdy jest ustawiony na Storyboardzie. Brak odpowiedzi na to pytanie, więc postanowiłem dodać własne.

Jest bardzo łatwy sposób na obejście tego: narysuj zaokrąglone rogi w funkcji drawRect(rect: CGRect).

Na przykład, gdybym chciał górne zaokrąglone rogi dla UIView, podklasowałbym UIView, a następnie użyj tej podklasy, gdzie jest to właściwe.

import UIKit

class TopRoundedView: UIView {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        var maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: UIRectCorner.TopLeft | UIRectCorner.TopRight, cornerRadii: CGSizeMake(5.0, 5.0))

        var maskLayer = CAShapeLayer()
        maskLayer.frame = self.bounds
        maskLayer.path = maskPath.CGPath

        self.layer.mask = maskLayer
    }
}

Jest to najlepszy sposób na przezwyciężenie problemu i nie zajmuje dużo czasu, aby się do niego dostosować.

 2
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-20 09:30:11

Zaokrąglanie tylko niektórych narożników nie będzie przyjemne z automatyczną zmianą rozmiaru lub automatycznym układem.

Więc inną opcją jest użycie zwykłego cornerRadius i ukrycie narożników, których nie chcesz pod innym widokiem lub poza jego granicami superview, upewniając się, że jest ustawiony tak, aby klipował jego zawartość.

 2
Author: Rivera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-12 13:11:31

Aby dodać do ODPOWIEDŹ i dodanie, stworzyłem prosty, wielokrotnego użytku UIView w języku Swift. W zależności od przypadku użycia, możesz chcieć wprowadzić modyfikacje (unikaj tworzenia obiektów na każdym układzie itp.), ale chciałem, aby było to tak proste, jak to tylko możliwe. Rozszerzenie pozwala zastosować to do innych widoków (np. UIImageView) łatwiej, jeśli nie lubisz podklasowania.

extension UIView {

    func roundCorners(_ roundedCorners: UIRectCorner, toRadius radius: CGFloat) {
        roundCorners(roundedCorners, toRadii: CGSize(width: radius, height: radius))
    }

    func roundCorners(_ roundedCorners: UIRectCorner, toRadii cornerRadii: CGSize) {
        let maskBezierPath = UIBezierPath(
            roundedRect: bounds,
            byRoundingCorners: roundedCorners,
            cornerRadii: cornerRadii)
        let maskShapeLayer = CAShapeLayer()
        maskShapeLayer.frame = bounds
        maskShapeLayer.path = maskBezierPath.cgPath
        layer.mask = maskShapeLayer
    }
}

class RoundedCornerView: UIView {

    var roundedCorners: UIRectCorner = UIRectCorner.allCorners
    var roundedCornerRadii: CGSize = CGSize(width: 10.0, height: 10.0)

    override func layoutSubviews() {
        super.layoutSubviews()
        roundCorners(roundedCorners, toRadii: roundedCornerRadii)
    }
}

Oto jak zastosujesz go do UIViewController:

class MyViewController: UIViewController {

    private var _view: RoundedCornerView {
        return view as! RoundedCornerView
    }

    override func loadView() {
        view = RoundedCornerView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        _view.roundedCorners = [.topLeft, .topRight]
        _view.roundedCornerRadii = CGSize(width: 10.0, height: 10.0)
    }
}
 1
Author: kgaidis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:36

Podsumowując odpowiedź Stuarta, można zastosować metodę zaokrąglania narożników w następujący sposób:

@implementation UIView (RoundCorners)

- (void)applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];

    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;

    self.layer.mask = maskLayer;
}

@end

Aby zastosować zaokrąglenie narożnika, wystarczy:

[self.imageView applyRoundCorners:UIRectCornerTopRight|UIRectCornerTopLeft radius:10];
 0
Author: Willy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-18 06:36:43