In UIView zwei Ecken abrunden

82

Vor einiger Zeit habe ich eine Frage zum Runden von nur zwei Ecken einer Ansicht gestellt und eine großartige Antwort erhalten, habe aber Probleme bei der Implementierung. Hier ist meine drawRect: Methode:

- (void)drawRect:(CGRect)rect {
    //[super drawRect:rect]; <------Should I uncomment this?
    int radius = 5;
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextBeginPath(context);
    CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI / 2, 1);
    CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
    CGContextClosePath(context);
    CGContextClip(context);
}

Die Methode wird aufgerufen, scheint jedoch das Ergebnis der Ansicht nicht zu beeinflussen. Irgendwelche Ideen warum?

Jumhyn
quelle

Antworten:

102

In iOS 11 eingeführte CACornerMask , mit deren Hilfe Sie oben links, oben rechts, unten links und unten rechts in der Ansichtsebene definieren können. Unten finden Sie ein Beispiel.

Hier versuche ich nur zwei obere Ecken abzurunden:

myView.clipsToBounds = true
myView.layer.cornerRadius = 10
myView.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]

Zu Ihrer Information Ref:

SachinVsSachin
quelle
Dies scheint der idiomatische Weg zu sein, dies jetzt zu tun. Guter Fang!
Jumhyn
2
Beachten Sie, dass die Breite / Höhe der Ansicht immer noch mindestens doppelt so groß sein muss wie der Eckenradius. Andernfalls treten seltsame Artefakte auf. Wenn Sie beispielsweise nur die unteren Ecken abrunden, ergibt ein Radius von 20 und eine Höhe von nur 30 eine sichtbar schmalere Zelle (um ein oder zwei Pixel).
Graham Perks
Beachten Sie, dass dies der richtige Weg ist, wenn Ihre Ansicht ihren Rahmen ändert (möglich aufgrund einer erweiterten UITextView, wie in meinem Fall - stackoverflow.com/questions/50926215/… ). Wenn Sie einen UIBezierPath festlegen, wird dieser nicht automatisch aktualisiert, wenn sich der Frame ändert.
Thomas
Es ist nur ab iOS 11 und höher verfügbarif #available(iOS 11.0, *) { }
Tri Ngo Minh
2
Gute Antwort. Es stellt sich heraus, dass die Einstellung clipsToBoundsauf truenicht erforderlich ist. Sie können Ecken maskieren und der Ebene Schatten hinzufügen, wenn dies der Fall clipsToBoundsist false. Kam für mich nützlich.
Au Ris
72


Soweit ich weiß, können Sie die CALayerMaskierung verwenden , wenn Sie auch die Unteransichten maskieren müssen. Es gibt zwei Möglichkeiten, dies zu tun. Der erste ist etwas eleganter, der zweite ist eine Problemumgehung :-) aber es ist auch schnell. Beide basieren auf CALayerMaskierung. Ich habe beide Methoden letztes Jahr in einigen Projekten angewendet, dann hoffe ich, dass Sie etwas Nützliches finden können.

Lösung 1

Zunächst habe ich diese Funktion erstellt, um im laufenden Betrieb eine Bildmaske ( UIImage) mit der abgerundeten Ecke zu erstellen, die ich benötige. Diese Funktion benötigt im Wesentlichen 5 Parameter: die Bildgrenzen und 4 Eckenradius (oben links, oben rechts, unten links und unten rechts).


static inline UIImage* MTDContextCreateRoundedMask( CGRect rect, CGFloat radius_tl, CGFloat radius_tr, CGFloat radius_bl, CGFloat radius_br ) {  

    CGContextRef context;
    CGColorSpaceRef colorSpace;

    colorSpace = CGColorSpaceCreateDeviceRGB();

    // create a bitmap graphics context the size of the image
    context = CGBitmapContextCreate( NULL, rect.size.width, rect.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast );

    // free the rgb colorspace
    CGColorSpaceRelease(colorSpace);    

    if ( context == NULL ) {
        return NULL;
    }

    // cerate mask

    CGFloat minx = CGRectGetMinX( rect ), midx = CGRectGetMidX( rect ), maxx = CGRectGetMaxX( rect );
    CGFloat miny = CGRectGetMinY( rect ), midy = CGRectGetMidY( rect ), maxy = CGRectGetMaxY( rect );

    CGContextBeginPath( context );
    CGContextSetGrayFillColor( context, 1.0, 0.0 );
    CGContextAddRect( context, rect );
    CGContextClosePath( context );
    CGContextDrawPath( context, kCGPathFill );

    CGContextSetGrayFillColor( context, 1.0, 1.0 );
    CGContextBeginPath( context );
    CGContextMoveToPoint( context, minx, midy );
    CGContextAddArcToPoint( context, minx, miny, midx, miny, radius_bl );
    CGContextAddArcToPoint( context, maxx, miny, maxx, midy, radius_br );
    CGContextAddArcToPoint( context, maxx, maxy, midx, maxy, radius_tr );
    CGContextAddArcToPoint( context, minx, maxy, minx, midy, radius_tl );
    CGContextClosePath( context );
    CGContextDrawPath( context, kCGPathFill );

    // Create CGImageRef of the main view bitmap content, and then
    // release that bitmap context
    CGImageRef bitmapContext = CGBitmapContextCreateImage( context );
    CGContextRelease( context );

    // convert the finished resized image to a UIImage 
    UIImage *theImage = [UIImage imageWithCGImage:bitmapContext];
    // image is retained by the property setting above, so we can 
    // release the original
    CGImageRelease(bitmapContext);

    // return the image
    return theImage;
}  

Jetzt brauchen Sie nur noch wenige Codezeilen. Ich habe Dinge in meine viewController- viewDidLoadMethode eingefügt , weil sie schneller ist, aber Sie können sie auch in Ihrer benutzerdefinierten Methode UIViewmit der layoutSubviewsMethode im Beispiel verwenden.



- (void)viewDidLoad {

    // Create the mask image you need calling the previous function
    UIImage *mask = MTDContextCreateRoundedMask( self.view.bounds, 50.0, 50.0, 0.0, 0.0 );
    // Create a new layer that will work as a mask
    CALayer *layerMask = [CALayer layer];
    layerMask.frame = self.view.bounds;       
    // Put the mask image as content of the layer
    layerMask.contents = (id)mask.CGImage;       
    // set the mask layer as mask of the view layer
    self.view.layer.mask = layerMask;              

    // Add a backaground color just to check if it works
    self.view.backgroundColor = [UIColor redColor];
    // Add a test view to verify the correct mask clipping
    UIView *testView = [[UIView alloc] initWithFrame:CGRectMake( 0.0, 0.0, 50.0, 50.0 )];
    testView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:testView];
    [testView release];

    [super viewDidLoad];
}

Lösung 2

Diese Lösung ist etwas "schmutziger". Im Wesentlichen können Sie eine Maskenebene mit der abgerundeten Ecke erstellen, die Sie benötigen (alle Ecken). Dann sollten Sie die Höhe der Maskenebene um den Wert des Eckenradius erhöhen. Auf diese Weise werden die unteren abgerundeten Ecken ausgeblendet und Sie können nur die obere abgerundete Ecke sehen. Ich habe den Code nur in die viewDidLoadMethode eingefügt, weil er schneller ist, aber Sie können ihn auch in Ihrer benutzerdefinierten Methode UIViewmit der layoutSubviewsMethode im Beispiel verwenden.

  

- (void)viewDidLoad {

    // set the radius
    CGFloat radius = 50.0;
    // set the mask frame, and increase the height by the 
    // corner radius to hide bottom corners
    CGRect maskFrame = self.view.bounds;
    maskFrame.size.height += radius;
    // create the mask layer
    CALayer *maskLayer = [CALayer layer];
    maskLayer.cornerRadius = radius;
    maskLayer.backgroundColor = [UIColor blackColor].CGColor;
    maskLayer.frame = maskFrame;

    // set the mask
    self.view.layer.mask = maskLayer;

    // Add a backaground color just to check if it works
    self.view.backgroundColor = [UIColor redColor];
    // Add a test view to verify the correct mask clipping
    UIView *testView = [[UIView alloc] initWithFrame:CGRectMake( 0.0, 0.0, 50.0, 50.0 )];
    testView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:testView];
    [testView release];

    [super viewDidLoad];
}

Hoffe das hilft. Ciao!

Lomanf
quelle
Endlich funktioniert! Vielen Dank! Was ist der Vorteil von einem gegenüber dem anderen?
Jumhyn
Auf jeden Fall ist sogar die CAShapeLayer-Lösung perfekt. Sie können den CGPath mit dem UIBezierPath (der 3.2+ benötigt) oder einfach mit den CGPath-Methoden (CGPathMoveToPoint, CGPathAddQuadCurveToPoint usw.)
zeichnen
1
Wie kann ich die Größe der Maske nach der Drehung vom Hoch- zum Querformat automatisch ändern?
Chourobin
Vergessen Sie nicht, layer.masksToBounds = YES
Dmitry
Wie kann man es schaffen, nur die oberen und unteren rechten Ecken abzurunden?
Pflug
69

Kämmerei durch die wenigen Antworten und Kommentare, fand ich heraus , dass die Verwendung UIBezierPath bezierPathWithRoundedRectund CAShapeLayerdie einfachste und direkteste Weg. Es ist vielleicht nicht für sehr komplexe Fälle geeignet, aber für gelegentliche Abrundungen von Ecken funktioniert es für mich schnell und reibungslos.

Ich hatte einen vereinfachten Helfer erstellt, der die entsprechende Ecke in der Maske festlegt:

-(void) setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
    UIBezierPath* rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(10.0, 10.0)];

    CAShapeLayer* shape = [[CAShapeLayer alloc] init];
    [shape setPath:rounded.CGPath];

    view.layer.mask = shape;
}

Um es zu verwenden, rufen Sie einfach mit der entsprechenden UIRectCorner-Enumeration auf, z.

[self setMaskTo:self.photoView byRoundingCorners:UIRectCornerTopLeft|UIRectCornerBottomLeft];

Bitte beachten Sie, dass ich damit Ecken von Fotos in einer gruppierten UITableViewCell abrunden kann. Der Radius von 10,0 funktioniert für mich einwandfrei, wenn Sie den Wert nur entsprechend ändern müssen.

BEARBEITEN: Beachten Sie einfach eine zuvor sehr ähnliche Antwort wie diese ( Link ). Sie können diese Antwort bei Bedarf weiterhin als zusätzliche Komfortfunktion verwenden.


BEARBEITEN: Gleicher Code wie die UIView-Erweiterung in Swift 3

extension UIView {
    func maskByRoundingCorners(_ masks:UIRectCorner, withRadii radii:CGSize = CGSize(width: 10, height: 10)) {
        let rounded = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: masks, cornerRadii: radii)

        let shape = CAShapeLayer()
        shape.path = rounded.cgPath

        self.layer.mask = shape
    }
}

Um es zu benutzen, rufen Sie einfach Folgendes maskByRoundingCorneran UIView:

view.maskByRoundingCorners([.topLeft, .bottomLeft])
PL
quelle
Ist dies möglich, während noch ein Rahmen hinzugefügt wird?
Unome
2
Ich musste einen Funktionsaufruf in DispatchQueue.main.async {} setzen, weil nur eine Ecke abgerundet wurde ...
Maksim Kniazev
1
@MaksimKniazev Ja, dies muss vom Hauptthread (UI) aufgerufen werden. Diese Regel gilt für alle Aktionen, die die Benutzeroberfläche berühren. Sie müssen vom Hauptthread aus aufgerufen werden, da sonst seltsame Dinge passieren können.
PL
1
nur eine Ecke scheint sich zu runden. versuchte @MaksimKniazev Lösung funktionierte auch nicht.
Gustavo_fringe
Ich habe das gleiche Problem wie @Gustavo_fringe, bei dem die obere rechte Ecke nicht an Grenzen innerhalb einer xib-Ansicht (in der Größe geändert) gekürzt / maskiert ist. Ich bin mir noch nicht sicher, wie ich das Problem beheben soll.
CyberMew
57

Ich konnte das alles nicht in einen Kommentar zu @ lomanfs Antwort einfügen. Also füge ich es als Antwort hinzu.

Wie @lomanf sagte, müssen Sie eine Ebenenmaske hinzufügen, um zu verhindern, dass Unterebenen außerhalb der Grenzen Ihres Pfads gezeichnet werden. Es ist jetzt jedoch viel einfacher. Solange Sie auf iOS 3.2 oder höher abzielen, müssen Sie kein Bild mit Quarz erstellen und als Maske festlegen. Sie können einfach ein CAShapeLayermit a erstellen UIBezierPathund dieses als Maske verwenden.

Stellen Sie bei der Verwendung von Ebenenmasken außerdem sicher, dass die zu maskierende Ebene beim Hinzufügen der Maske nicht Teil einer Ebenenhierarchie ist. Ansonsten ist das Verhalten undefiniert. Wenn sich Ihre Ansicht bereits in der Hierarchie befindet, müssen Sie sie aus der Übersicht entfernen, maskieren und wieder an ihren ursprünglichen Platz zurücksetzen.

CAShapeLayer *maskLayer = [CAShapeLayer layer];
UIBezierPath *roundedPath = 
  [UIBezierPath bezierPathWithRoundedRect:maskLayer.bounds
                        byRoundingCorners:UIRectCornerTopLeft |
                                          UIRectCornerBottomRight
                              cornerRadii:CGSizeMake(16.f, 16.f)];    
maskLayer.fillColor = [[UIColor whiteColor] CGColor];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [roundedPath CGPath];

//Don't add masks to layers already in the hierarchy!
UIView *superview = [self.view superview];
[self.view removeFromSuperview];
self.view.layer.mask = maskLayer;
[superview addSubview:self.view];

Aufgrund der Funktionsweise des Renderns von Core-Animationen ist das Maskieren relativ langsam. Jede Maske erfordert einen zusätzlichen Rendering-Durchgang. Verwenden Sie Masken daher sparsam.

Einer der besten Teile dieses Ansatzes ist, dass Sie keine benutzerdefinierten UIViewElemente mehr erstellen und überschreiben müssen drawRect:. Dies sollte Ihren Code einfacher und vielleicht sogar schneller machen.

Nathan Eror
quelle
Scheint vielversprechend, aber die Ansicht erscheint nicht. Irgendwelche Ideen warum?
Jumhyn
10
Sie verwenden maskLayer.bounds, um die Größe des UIBezierPath zu bestimmen, aber der Wert der Grenzen ist CGRectZero. Sie müssen eine zweite Codezeile hinzufügen (unmittelbar nach der CAShapeLayer-Initialisierung) wie maskLayer.frame = self.view.bounds;
Lomanf
1
Ja, du hast Recht. Ich habe diesen Code aus einem anderen Beispiel extrahiert, in dem die Formebene bereits vorhanden war. Das tut mir leid.
Nathan Eror
Es funktioniert perfekt! Das Hinzufügen von maskLayer.frame = self.view.bounds, wie oben angegeben, funktioniert. Andernfalls werden die Ansichten nicht angezeigt.
Daniel Ribeiro
+1 Das funktioniert ganz gut. Tatsächlich denke ich, dass ich diese Methode der Lösung Nr. 2 von Lomanf vorziehe. Mit dieser Methode können Sie problemlos Bögen zu gegenüberliegenden Ecken hinzufügen (z. B. oben rechts + unten links), und sie ist sicherlich kürzer als die erste Lösung von Lomanf. Großartiger Code Nathan!
FreeAsInBeer
30

Ich habe Nathans Beispiel genommen und eine Kategorie erstellt UIView, damit man sich an die DRY-Prinzipien halten kann. Ohne weiteres:

UIView + Roundify.h

#import <UIKit/UIKit.h>

@interface UIView (Roundify)

-(void)addRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii;
-(CALayer*)maskForRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii;

@end

UIView + Roundify.m

#import "UIView+Roundify.h"

@implementation UIView (Roundify)

-(void)addRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
    CALayer *tMaskLayer = [self maskForRoundedCorners:corners withRadii:radii];
    self.layer.mask = tMaskLayer;
}

-(CALayer*)maskForRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = self.bounds;

    UIBezierPath *roundedPath = [UIBezierPath bezierPathWithRoundedRect:
        maskLayer.bounds byRoundingCorners:corners cornerRadii:radii];
    maskLayer.fillColor = [[UIColor whiteColor] CGColor];
    maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
    maskLayer.path = [roundedPath CGPath];

    return maskLayer;
}

@end

Anrufen:

[myView addRoundedCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight
                withRadii:CGSizeMake(20.0f, 20.0f)];
FreeAsInBeer
quelle
2
Vergessen Sie nicht, <QuartzCore / QuatzCore.h> in die .m-Datei zu importieren und QuartzCore.framework zum Projekt hinzuzufügen
DEzra
+1 Tolles Zeug, eine Frage, was ist der Zweck, die Ansicht zu entfernen / in die Übersicht zu lesen?
Eladleb
@eladleb: Der Basiscode hier war von Nathan, ich habe ihn einfach ein bisschen optimiert. Aufgrund meiner Recherchen kann ich keinen gültigen Grund finden, die Ansicht vor dem Anwenden der Ebenenmaske zu entfernen. Daher glaube ich, dass es sicher ist, diesen Code wegzulassen.
FreeAsInBeer
@FreeAsInBeer - Ich stimme zu, dann würde ich dafür stimmen, es zu entfernen, dies ändert tatsächlich die Ansicht Z-Reihenfolge und kann weitere Probleme verursachen ..
Eladleb
Das Problem beim Hinzufügen abgerundeter Ecken zu einer Tabellenansicht wurde behoben: Ich fügte eine Kopfzeile, abgerundete Ecken und eine Fußzeile mit abgerundeten unteren Ecken hinzu. Vielen Dank.
Nightfixed
24

Um die Antwort von PL ein wenig zu erweitern, habe ich die Methode so umgeschrieben, dass bestimmte Objekte nicht UIButtonrichtig gerundet wurden

- (void)setMaskTo:(id)sender byRoundingCorners:(UIRectCorner)corners withCornerRadii:(CGSize)radii
{
    // UIButton requires this
    [sender layer].cornerRadius = 0.0;

    UIBezierPath *shapePath = [UIBezierPath bezierPathWithRoundedRect:[sender bounds]
                                                byRoundingCorners:corners
                                                cornerRadii:radii];

    CAShapeLayer *newCornerLayer = [CAShapeLayer layer];
    newCornerLayer.frame = [sender bounds];
    newCornerLayer.path = shapePath.CGPath;
    [sender layer].mask = newCornerLayer;
}

Und nenn es vorbei

[self setMaskTo:self.continueButton byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight withCornerRadii:CGSizeMake(3.0, 3.0)];
Josh Valdivieso
quelle
12

Wenn Sie dies in Swift tun möchten, können Sie eine Erweiterung von a verwenden UIView. Auf diese Weise können alle Unterklassen die folgende Methode verwenden:

import QuartzCore

extension UIView {
    func roundCorner(corners: UIRectCorner, radius: CGFloat) {
        let maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = maskPath.CGPath
        layer.mask = maskLayer
    }
}    

Anwendungsbeispiel:

self.anImageView.roundCorner(.topRight, radius: 10)
Kevin Delord
quelle
2
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(5, 5, self.bounds.size.width-10, self.bounds.size.height-10)
                                               byRoundingCorners:UIRectCornerAllCorners
                                                     cornerRadii:CGSizeMake(12.0, 12.0)];

Ändern Sie "AllCorners" entsprechend Ihren Anforderungen.

Umang
quelle
Müssen Sie dies in UIView tun oder kann dies in UIViewController erfolgen? Was machst du mit dem UIBezierPath, wenn du es geschafft hast? Vielen Dank.
StoneBreaker
2

Alle angebotenen Lösungen erreichen das Ziel. Aber UIConstraintskann das manchmal in die Luft jagen.

Zum Beispiel müssen die unteren Ecken abgerundet sein. Wenn für die UIView, die gerundet werden soll, die Einschränkung für Höhe oder unteren Abstand festgelegt ist, müssen die Codefragmente, die die Ecken abrunden, in die viewDidLayoutSubviewsMethode verschoben werden.

Hervorheben:

UIBezierPath *maskPath = [UIBezierPath
    bezierPathWithRoundedRect:roundedView.bounds byRoundingCorners:
    (UIRectCornerTopRight | UIRectCornerBottomRight) cornerRadii:CGSizeMake(16.0, 16.0)];

Das obige Codefragment rundet die obere rechte Ecke nur ab, wenn dieser Code aktiviert ist viewDidLoad. Weil roundedView.boundssich ändern wird, nachdem die Einschränkungen aktualisiert wurden UIView.

hasan
quelle
2

Erweitern Sie die akzeptierte Antwort und fügen Sie Abwärtskompatibilität hinzu. Vor iOS 11 ist view.layer.maskedCorners nicht verfügbar. So können wir das machen

if #available(iOS 11.0, *) {
    myView.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]
} else {
    myView.maskByRoundingCorners([.topLeft, .topRight])
}

extension UIView{
    func maskByRoundingCorners(_ masks:UIRectCorner, withRadii radii:CGSize = CGSize(width: 10, height: 10)) {
        let rounded = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: masks, cornerRadii: radii)

        let shape = CAShapeLayer()
        shape.path = rounded.cgPath

        self.layer.mask = shape
    }
}

Wir haben maskByRoundingCorners als UIView-Erweiterung geschrieben, um die Wiederverwendung von Code zu verbessern.

Credits an @SachinVsSachin und @PL :) Ich habe ihre Codes kombiniert, um es besser zu machen.

Rizwan Ahmed
quelle
1

Erstellen Sie eine Maske und legen Sie sie auf der Ebene der Ansicht fest

Steve Riggins
quelle
Wie würde ich das machen? Ich bin etwas wackelig, was die Ansichtsverwaltung auf niedrigerer Ebene angeht.
Jumhyn
1
stackoverflow.com/questions/2264083/… könnte Ihnen helfen
Steve Riggins
1
Ich bin froh zu sehen, dass meine Antwort immer noch Menschen hilft!
Nevan King
1

Beginnend mit Ihrem Code können Sie sich für etwas wie das folgende Snippet entscheiden.

Ich bin mir nicht sicher, ob dies das Ergebnis ist, nach dem Sie suchen. Erwähnenswert ist auch, dass sich das System sehr seltsam verhält, wenn das System drawRect aufruft: Auch hier wird nur ein Teil des Rect neu gezeichnet. Der oben erwähnte Ansatz von Nevan könnte ein besserer Weg sein.

 // make sure the view's background is set to [UIColor clearColor]
- (void)drawRect:(CGRect)rect
{
    CGFloat radius = 10.0;
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, rect.size.width/2, rect.size.height/2);
    CGContextRotateCTM(context, M_PI); // rotate so image appears right way up
    CGContextTranslateCTM(context, -rect.size.width/2, -rect.size.height/2);

    CGContextBeginPath(context);

    CGContextMoveToPoint(context, rect.origin.x, rect.origin.y);
    CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI / 2, 1);
    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);
    CGContextClip(context); 

    // now do your drawing, e.g. draw an image
    CGImageRef anImage = [[UIImage imageNamed:@"image.jpg"] CGImage];
    CGContextDrawImage(context, rect, anImage);    
}
Schräg
quelle
Ich habe eine Ansicht mit abgerundeten Ecken ausgearbeitet, aber ich kann sie nicht dazu bringen, eine ihrer Unteransichten zu beschneiden. Wenn ich eine Ansicht mit quadratischen Ecken am Rand meiner Ansicht platziere, werden die abgerundeten Ecken der Übersicht immer noch verdeckt. Irgendeine Idee, warum das passiert?
Jumhyn
Da das Zuschneiden -drawRect:nur die Ansicht betrifft, die Sie zeichnen. Die Unteransichten werden nach Abschluss Ihrer Zeichnung unabhängig voneinander gezeichnet.
Jonathan Grynspan
1

Ein etwas hackiger, aber relativ einfacher Weg (keine Unterklassen, Maskierung usw.) besteht darin, zwei UIViews zu haben. Beides mit clipToBounds = YES. Stellen Sie abgerundete Ecken in der untergeordneten Ansicht ein und positionieren Sie sie dann in der übergeordneten Ansicht, sodass die gewünschten Ecken zugeschnitten werden.

UIView* parent = [[UIView alloc] initWithFrame:CGRectMake(10,10,100,100)];
parent.clipsToBounds = YES;

UIView* child = [[UIView alloc] new];
child.clipsToBounds = YES;
child.layer.cornerRadius = 3.0f;
child.backgroundColor = [UIColor redColor];
child.frame = CGRectOffset(parent.bounds, +4, -4);


[parent addSubView:child];

Unterstützt nicht den Fall, in dem zwei diagonal gegenüberliegende Ecken abgerundet sein sollen.

William Denniss
quelle
1

Der Bezier-Pfad ist die Antwort. Wenn Sie zusätzlichen Code benötigen, hat dieser für mich funktioniert: https://stackoverflow.com/a/13163693/936957

UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:_backgroundImageView.bounds 
                                 byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) 
                                       cornerRadii:CGSizeMake(3.0, 3.0)];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
_backgroundImageView.layer.mask = maskLayer;
[maskLayer release];
Yunus Nedim Mehel
quelle
1

UIBezierPath-Lösung.

- (void) drawRect:(CGRect)rect {

    [super drawRect:rect];

    //Create shape which we will draw. 
    CGRect rectangle = CGRectMake(2, 
                                  2, 
                                  rect.size.width - 4,
                                  rect.size.height - 4);

    //Create BezierPath with round corners
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rectangle 
                                                   byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight 
                                                         cornerRadii:CGSizeMake(10.0, 10.0)];

    //Set path width
    [maskPath setLineWidth:2];

    //Set color
    [[UIColor redColor] setStroke];

    //Draw BezierPath to see it
    [maskPath stroke];
}
Alexey Bondarchuk
quelle
0

Dies kann nur funktionieren, wenn einige Dinge richtig eingestellt sind:

  1. clipsToBounds muss auf YES gesetzt sein
  2. undurchsichtig muss NEIN sein
  3. backgroundColor sollte "clearColor" sein (da bin ich mir nicht ganz sicher)
  4. contentMode muss "UIContentModeRedraw" sein, da drawRect nicht oft aufgerufen wird, wenn dies nicht der Fall ist
  5. [super drawRect: rect] muss nach dem CGContextClip aufgerufen werden
  6. Ihre Ansicht enthält möglicherweise keine beliebigen Unteransichten (diesbezüglich nicht sicher).
  7. Stellen Sie sicher, dass Sie mindestens einmal "needDisplay:" einstellen, um Ihre Zeichnung auszulösen
Dunkelstern
quelle
0

Mir ist klar, dass Sie versuchen, die beiden oberen Ecken eines UITableView abzurunden, aber aus irgendeinem Grund habe ich festgestellt, dass die beste Lösung darin besteht, Folgendes zu verwenden:

self.tableView.layer.cornerRadius = 10;

Programmatisch sollte es alle vier Ecken abrunden, aber aus irgendeinem Grund rundet es nur die beiden oberen Ecken ab. ** Bitte sehen Sie den Screenshot unten, um die Wirkung des Codes zu sehen, den ich oben geschrieben habe.

Ich hoffe das hilft!

Geben Sie hier die Bildbeschreibung ein

Adam Cooper
quelle
1
Wenn nicht alle Ecken des abgerundet werden, UITableViewmuss an anderer Stelle ein Code vorhanden sein, der dafür sorgt, dass die UITableViewEcken unten nicht abgerundet sind, oder vielleicht sogar ein UIViewoder UILabeldas den unteren Teil des abdeckt UITableView.
Josh Valdivieso
-1

Sie müssen wahrscheinlich an Grenzen klammern. Fügen Sie die Zeile hinzu

self.clipsToBounds = YES

irgendwo im Code, um diese Eigenschaft festzulegen.

Ned
quelle