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?
ios
objective-c
iphone
rounded-corners
Jumhyn
quelle
quelle
if #available(iOS 11.0, *) { }
clipsToBounds
auftrue
nicht erforderlich ist. Sie können Ecken maskieren und der Ebene Schatten hinzufügen, wenn dies der FallclipsToBounds
istfalse
. Kam für mich nützlich.Soweit ich weiß, können Sie die
CALayer
Maskierung 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 aufCALayer
Maskierung. 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-
viewDidLoad
Methode eingefügt , weil sie schneller ist, aber Sie können sie auch in Ihrer benutzerdefinierten MethodeUIView
mit derlayoutSubviews
Methode 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
viewDidLoad
Methode eingefügt, weil er schneller ist, aber Sie können ihn auch in Ihrer benutzerdefinierten MethodeUIView
mit derlayoutSubviews
Methode 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!
quelle
Kämmerei durch die wenigen Antworten und Kommentare, fand ich heraus , dass die Verwendung
UIBezierPath bezierPathWithRoundedRect
undCAShapeLayer
die 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
maskByRoundingCorner
anUIView
:quelle
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
CAShapeLayer
mit a erstellenUIBezierPath
und 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
UIView
Elemente mehr erstellen und überschreiben müssendrawRect:
. Dies sollte Ihren Code einfacher und vielleicht sogar schneller machen.quelle
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)];
quelle
Um die Antwort von PL ein wenig zu erweitern, habe ich die Methode so umgeschrieben, dass bestimmte Objekte nicht
UIButton
richtig 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)];
quelle
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)
quelle
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.
quelle
Alle angebotenen Lösungen erreichen das Ziel. Aber
UIConstraints
kann das manchmal in die Luft jagen.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
. WeilroundedView.bounds
sich ändern wird, nachdem die Einschränkungen aktualisiert wurdenUIView
.quelle
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.
quelle
Erstellen Sie eine Maske und legen Sie sie auf der Ebene der Ansicht fest
quelle
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); }
quelle
-drawRect:
nur die Ansicht betrifft, die Sie zeichnen. Die Unteransichten werden nach Abschluss Ihrer Zeichnung unabhängig voneinander gezeichnet.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.
quelle
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];
quelle
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]; }
quelle
Dies kann nur funktionieren, wenn einige Dinge richtig eingestellt sind:
quelle
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!
quelle
UITableView
muss an anderer Stelle ein Code vorhanden sein, der dafür sorgt, dass dieUITableView
Ecken unten nicht abgerundet sind, oder vielleicht sogar einUIView
oderUILabel
das den unteren Teil des abdecktUITableView
.Sie müssen wahrscheinlich an Grenzen klammern. Fügen Sie die Zeile hinzu
self.clipsToBounds = YES
irgendwo im Code, um diese Eigenschaft festzulegen.
quelle