Comment définir cornerRadius pour seulement le coin supérieur gauche et supérieur droit d'un UIView?

Existe-t-il un moyen de définir cornerRadius uniquement pour les coins supérieur gauche et supérieur droit d'un UIView?

J'ai essayé ce qui suit, mais il finit par ne plus voir la vue.

UIView *view = [[UIView alloc] initWithFrame:frame];

CALayer *layer = [CALayer layer];
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
layer.shadowPath = shadowPath.CGPath;
view.layer.mask = layer;
289
demandé sur Cœur 2012-04-16 03:58:26

18 réponses

Faites attention au fait que si vous avez des contraintes de mise en page attachées, vous devez l'actualiser comme suit dans votre sous-classe UIView:

func layoutSubviews() {
    super.layoutSubviews()
    roundCorners(corners: [.topLeft, .topRight], radius: 3.0)
}

Si vous ne faites pas cela, il n'apparaîtra pas.


Et à l'extension de coin rond:

extension UIView {
   func roundCorners(corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
}
7
répondu Stéphane de Luca 2018-09-23 16:55:02

Je ne sais pas pourquoi votre solution n'a pas fonctionné mais le code suivant fonctionne pour moi. Créez un masque de Bézier et appliquez-le à votre vue. Dans mon code ci-dessous, j'arrondissais les coins inférieurs du _backgroundView avec un rayon de 3 pixels. self est une coutume UITableViewCell:

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

CAShapeLayer *maskLayer = [CAShapeLayer layer];

maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;

self.backgroundImageView.layer.mask = maskLayer;

Version Swift avec quelques améliorations:

let path = UIBezierPath(roundedRect:viewToRound.bounds, byRoundingCorners:[.TopRight, .BottomLeft], cornerRadii: CGSizeMake(20, 20))
let maskLayer = CAShapeLayer()

maskLayer.path = path.CGPath
viewToRound.layer.mask = maskLayer

Version Swift 3.0:

let path = UIBezierPath(roundedRect:viewToRound.bounds,
                        byRoundingCorners:[.topRight, .bottomLeft],
                        cornerRadii: CGSize(width: 20, height:  20))

let maskLayer = CAShapeLayer()

maskLayer.path = path.cgPath
viewToRound.layer.mask = maskLayer

Extension Swift ici

497
répondu Yunus Nedim Mehel 2017-05-23 12:02:56

Voici uneSwift version de @JohnnyRockex réponse

extension UIView {

    func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
         let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
         let mask = CAShapeLayer()
         mask.path = path.cgPath
         self.layer.mask = mask
    }

}

view.roundCorners([.topLeft, .bottomRight], radius: 10)

Note

Si vous utilisez Auto Layout , vous devrez sous-classer votre UIView et appeler roundCorners dans la vue layoutSubviews pour un effet optimal.

class View: UIView {
    override func layoutSubviews() {
        super.layoutSubviews()

        self.roundCorners([.topLeft, .bottomLeft], radius: 10)
    }
}
218
répondu Arbitur 2017-09-02 05:07:44

Exemple de Code Swift ici: https://stackoverflow.com/a/35621736/308315


Pas directement. Vous devrez:

  1. Créer un CAShapeLayer
  2. Définir son path un CGPathRef basée sur view.bounds, mais avec seulement deux coins arrondis (probablement en utilisant +[UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:])
  3. Définissez votre view.layer.mask sur CAShapeLayer
97
répondu Kurt Revis 2017-05-23 11:55:10

Voici une méthode courte implémentée comme ceci:

- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *openInMaps = [UIButton new];
    [openInMaps setFrame:CGRectMake(15, 135, 114, 70)];
    openInMaps = (UIButton *)[self roundCornersOnView:openInMaps onTopLeft:NO topRight:NO bottomLeft:YES bottomRight:NO radius:5.0];
}

- (UIView *)roundCornersOnView:(UIView *)view onTopLeft:(BOOL)tl topRight:(BOOL)tr bottomLeft:(BOOL)bl bottomRight:(BOOL)br radius:(float)radius {

    if (tl || tr || bl || br) {
        UIRectCorner corner = 0;
        if (tl) {corner = corner | UIRectCornerTopLeft;}
        if (tr) {corner = corner | UIRectCornerTopRight;}
        if (bl) {corner = corner | UIRectCornerBottomLeft;}
        if (br) {corner = corner | UIRectCornerBottomRight;}

        UIView *roundedView = view;
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:roundedView.bounds byRoundingCorners:corner cornerRadii:CGSizeMake(radius, radius)];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.frame = roundedView.bounds;
        maskLayer.path = maskPath.CGPath;
        roundedView.layer.mask = maskLayer;
        return roundedView;
    }
    return view;
}
59
répondu Johnny Rockex 2017-08-23 10:46:15

Et enfin ... il y a CACornerMask dans iOS11! Avec CACornerMask cela peut être fait assez facilement:

let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 10
view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] // Top right corner, Top left corner respectively
57
répondu belous 2018-04-14 13:28:02

Essayez ce code,

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:( UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(5.0, 5.0)];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.view.bounds;
maskLayer.path  = maskPath.CGPath;

view.layer.mask = maskLayer;
15
répondu 2015-09-18 09:35:48

Emma: .TopRight et .BottomRight ne fonctionnent pas pour vous peut-être parce que l'appel à view.roundCorners est fait avant que les view bounds finaux ne soient calculés. Notez que le Bezier Path dérive des limites de vue au moment où il est appelé. Par exemple, si la mise en page automatique réduit la vue, les coins arrondis sur le côté droit peuvent être en dehors de la vue. Essayez de l'appeler dans viewDidLayoutSubviews, où la limite de la vue est finale.

13
répondu David Barta 2018-09-01 01:28:14

IOS 11 , Swift 4
Et vous pouvez essayer ce code:

if #available(iOS 11.0, *) {
   element.clipsToBounds = true
   element.layer.cornerRadius = CORNER_RADIUS
   element.layer.maskedCorners = [.layerMaxXMaxYCorner]
} else {
   // Fallback on earlier versions
}

Et vous pouvez l'utiliser dans la cellule de vue de table.

6
répondu reza_khalafi 2018-02-27 07:10:10

Un moyen de le faire par programmation serait de créer un UIView sur la partie supérieure de la UIView qui a les coins arrondis. Ou vous pourriez cacher le haut sous quelque chose.

5
répondu Nate Symer 2013-07-16 01:55:45
    // Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds 
                           byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) 
                           cornerRadii:CGSizeMake(7.0, 7.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.stripBlackImnageView.bounds;
maskLayer.path = maskPath.CGPath; 
// Set the newly created shapelayer as the mask for the image view's layer
view.layer.mask = maskLayer;
5
répondu Spydy 2015-09-22 07:41:54

Dans swift 4.1 et Xcode 9.4.1 à partir de ce code est suffisant

//In viewDidLoad
if #available(iOS 11.0, *){
        detailsSubView.clipsToBounds = false
        detailsSubView.layer.cornerRadius = 10
        detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaXMinYCorner]
} else {
      //For lower versions
}

, Mais pour les versions inférieures

let rectShape = CAShapeLayer()
    rectShape.bounds = detailsSubView.frame
    rectShape.position = detailsSubView.center
    rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
    detailsSubView.layer.mask = rectShape

Si vous utilisez AutoResizing dans storyboard écrivez ceci dans viewDidLayoutSubviews

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

if #available(iOS 11.0, *){
        detailsSubView.clipsToBounds = false
        detailsSubView.layer.cornerRadius = 10
        detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
    }else{
        let rectShape = CAShapeLayer()
        rectShape.bounds = detailsSubView.frame
        rectShape.position = detailsSubView.center
        rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
        detailsSubView.layer.mask = rectShape
    }
}
4
répondu iOS 2018-10-05 06:24:35

Toutes les réponses déjà données sont vraiment bonnes et valides (en particulier l'idée de Yunus d'utiliser la propriété mask).

Cependant, j'avais besoin de quelque chose d'un peu plus complexe car ma couche pouvait souvent changer de taille, ce qui signifiait que j'avais besoin d'appeler cette logique de masquage à chaque fois et c'était un peu ennuyeux.

J'ai utilisé swift extensions et les propriétés calculées pour construire une vraie propriété cornerRadii qui prend en charge la mise à jour automatique du masque lorsque la couche est posée.

Ceci a été réalisé en utilisant Peter Steinberg Grande Aspects bibliothèque pour swizzling.

Le code complet est ici:

extension CALayer {
  // This will hold the keys for the runtime property associations
  private struct AssociationKey {
    static var CornerRect:Int8 = 1    // for the UIRectCorner argument
    static var CornerRadius:Int8 = 2  // for the radius argument
  }

  // new computed property on CALayer
  // You send the corners you want to round (ex. [.TopLeft, .BottomLeft])
  // and the radius at which you want the corners to be round
  var cornerRadii:(corners: UIRectCorner, radius:CGFloat) {
    get {
      let number = objc_getAssociatedObject(self, &AssociationKey.CornerRect)  as? NSNumber ?? 0
      let radius = objc_getAssociatedObject(self, &AssociationKey.CornerRadius)  as? NSNumber ?? 0
      return (corners: UIRectCorner(rawValue: number.unsignedLongValue), radius: CGFloat(radius.floatValue))
    }
    set (v) {
      let radius = v.radius
      let closure:((Void)->Void) = {
        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: v.corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.CGPath
        self.mask = mask
      }
      let block: @convention(block) Void -> Void = closure
      let objectBlock = unsafeBitCast(block, AnyObject.self)
      objc_setAssociatedObject(self, &AssociationKey.CornerRect, NSNumber(unsignedLong: v.corners.rawValue), .OBJC_ASSOCIATION_RETAIN)
      objc_setAssociatedObject(self, &AssociationKey.CornerRadius, NSNumber(float: Float(v.radius)), .OBJC_ASSOCIATION_RETAIN)
      do { try aspect_hookSelector("layoutSublayers", withOptions: .PositionAfter, usingBlock: objectBlock) }
      catch _ { }
    }
  }
}

J'ai écrit un article de blog simple expliquant cela.

3
répondu apouche 2015-09-19 14:10:41

Le moyen le plus simple serait de faire un masque avec une couche de coin arrondie.

CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0,0,maskWidth ,maskHeight);
maskLayer.contents = (__bridge id)[[UIImage imageNamed:@"maskImageWithRoundedCorners.png"] CGImage];

aUIView.layer.mask = maskLayer;

Et n'oubliez pas de:

#import <QuartzCore/QuartzCore.h>
2
répondu Matt Hudson 2012-04-16 00:11:44

C'est comment vous pouvez définir un rayon d'angle pour chaque angle d'un bouton avec Xamarin en C#:

var maskPath = UIBezierPath.FromRoundedRect(MyButton.Bounds, UIRectCorner.BottomLeft | UIRectCorner.BottomRight,
    new CGSize(10.0, 10.0));
var maskLayer = new CAShapeLayer
{
    Frame = MyButton.Bounds,
    Path = maskPath.CGPath
};
MyButton.Layer.Mask = maskLayer;
2
répondu jfmg 2016-04-22 08:31:11

Swift 4

extension UIView {

    func roundTop(radius:CGFloat = 5){
        self.clipsToBounds = true
        self.layer.cornerRadius = radius
        if #available(iOS 11.0, *) {
            self.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
        } else {
            // Fallback on earlier versions
        }
    }

    func roundBottom(radius:CGFloat = 5){
        self.clipsToBounds = true
        self.layer.cornerRadius = radius
        if #available(iOS 11.0, *) {
            self.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
        } else {
            // Fallback on earlier versions
        }
    }
}
2
répondu MrMins 2018-08-02 00:18:44

Une belle extension pour réutiliser la solution Yunus Nedim Mehel

Swift 2.3

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

L'Utilisation de

let view = UIView()
view.roundCornersWithLayerMask(10,[.TopLeft,.TopRight])
1
répondu apinho 2016-11-02 15:19:48

Swift 4 moyen facile en 1 ligne

Usage
//MARK:- Corner Radius of only two side of UIViews
    self.roundCorners(view: yourview, corners: [.bottomLeft, .topRight], radius: 12.0)

Functin
//MARK:- Corner Radius of only two side of UIViews
func roundCorners(view :UIView, corners: UIRectCorner, radius: CGFloat){
        let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        view.layer.mask = mask
}
1
répondu Shakeel Ahmed 2018-09-06 11:08:57