web-dev-qa-db-fra.com

Ajouter une bordure en dehors d'un UIView (au lieu de l'intérieur)

Si vous ajoutez une bordure d’une vue à l’aide de code dans une vue telle que

self.layer.borderColor = [UIColor yellowColor].CGColor;
self.layer.borderWidth = 2.0f;

la bordure est ajoutée à l'intérieur de la vue comme suit: enter image description here

la vue de droite est la vue d'origine, comme vous pouvez le constater, la zone noire de la vue avec bordure est inférieure à celle d'origine. mais ce que je veux obtenir, c’est une bordure en dehors de la vue originale, comme ceci:enter image description here. la zone noire est égale à celle d'origine, comment puis-je l'implémenter?

71
remykits

Malheureusement, il n'y a pas simplement une petite propriété que vous pouvez définir pour aligner la bordure sur l'extérieur. Il dessine aligné à l'intérieur car les opérations de dessin par défaut d'UIViews sont dessinées dans ses limites.

La solution la plus simple qui me vienne à l’esprit serait d’agrandir UIView de la taille de la largeur de la bordure lors de l’application de la bordure:

CGFloat borderWidth = 2.0f;

self.frame = CGRectInset(self.frame, -borderWidth, -borderWidth);
self.layer.borderColor = [UIColor yellowColor].CGColor;
self.layer.borderWidth = borderWidth;
93

Ok, il y a déjà une réponse acceptée mais je pense qu'il y a une meilleure façon de le faire, il vous suffit d'avoir un nouveau calque un peu plus grand que votre vue et de ne pas le masquer aux limites du calque de la vue (qui est en fait le comportement par défaut). Voici l exemple de code :

CALayer * externalBorder = [CALayer layer];
externalBorder.frame = CGRectMake(-1, -1, myView.frame.size.width+2, myView.frame.size.height+2);
externalBorder.borderColor = [UIColor blackColor].CGColor;
externalBorder.borderWidth = 1.0;

[myView.layer addSublayer:externalBorder];
myView.layer.masksToBounds = NO;

Bien sûr, si vous voulez que votre bordure ait 1 unité de large, si vous en voulez plus, vous adaptez la variable borderWidth et le cadre du calque en conséquence ... C'est mieux que d'utiliser une seconde vue un peu plus grande, car une CALayer est plus claire. que UIView et que vous n’ayez pas à modifier le cadre de myView, ce qui est bien, par exemple, si myView est une UIImageView 

N.B: Pour moi, le résultat n'était pas parfait sur simulateur (le calque n'était pas exactement à la bonne position, donc il était parfois plus épais sur un côté), mais correspondait exactement à ce qui était demandé sur un appareil réel.

MODIFIER 

En fait, le problème dont je parle dans le N.B était simplement parce que j'avais réduit l'écran du simulateur. En taille normale, il n'y a absolument aucun problème.

J'espère que ça aide

17
Hugues Duvillier

Eh bien, il n’existe aucune méthode directe pour le faireVous pouvez envisager certaines solutions de contournement.

  1. Changez et agrandissez le cadre et ajoutez le bordercolor comme vous l'avez fait
  2. Ajoutez une vue derrière la vue en cours avec la taille la plus grande afin qu'elle apparaisse comme une bordure. Peut être traitée comme une classe de vue personnalisée
  3. Si vous n’avez pas besoin d’une bordure définie (bordure dégagée), vous pouvez vous en remettre à l’ombre

    [view1 setBackgroundColor:[UIColor blackColor]];
    UIColor *color = [UIColor yellowColor];
    view1.layer.shadowColor = [color CGColor];
    view1.layer.shadowRadius = 10.0f;
    view1.layer.shadowOpacity = 1;
    view1.layer.shadowOffset = CGSizeZero;
    view1.layer.masksToBounds = NO;
    
13
Lithu T.V

Avec la meilleure réponse acceptée ci-dessus, j’ai fait des expériences avec de tels résultats pas sympas et des avantages inesthétiques:

 border without bezier path

Je partagerai donc avec vous mon extension UIView Swift, qui utilise plutôt UIBezierPath comme contour de bordure - sans bords inesthétiques (inspiré par @Fattie ):

 border with bezier path

//  UIView+BezierPathBorder.Swift

import UIKit

extension UIView {

    fileprivate var bezierPathIdentifier:String { return "bezierPathBorderLayer" }

    fileprivate var bezierPathBorder:CAShapeLayer? {
        return (self.layer.sublayers?.filter({ (layer) -> Bool in
            return layer.name == self.bezierPathIdentifier && (layer as? CAShapeLayer) != nil
        }) as? [CAShapeLayer])?.first
    }

    func bezierPathBorder(_ color:UIColor = .white, width:CGFloat = 1) {

        var border = self.bezierPathBorder
        let path = UIBezierPath(roundedRect: self.bounds, cornerRadius:self.layer.cornerRadius)
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask

        if (border == nil) {
            border = CAShapeLayer()
            border!.name = self.bezierPathIdentifier
            self.layer.addSublayer(border!)
        }

        border!.frame = self.bounds
        let pathUsingCorrectInsetIfAny =
            UIBezierPath(roundedRect: border!.bounds, cornerRadius:self.layer.cornerRadius)

        border!.path = pathUsingCorrectInsetIfAny.cgPath
        border!.fillColor = UIColor.clear.cgColor
        border!.strokeColor = color.cgColor
        border!.lineWidth = width * 2
    }

    func removeBezierPathBorder() {
        self.layer.mask = nil
        self.bezierPathBorder?.removeFromSuperlayer()
    }

}

Exemple:

let view = UIView(frame: CGRect(x: 20, y: 20, width: 100, height: 100))
view.layer.cornerRadius = view.frame.width / 2
view.backgroundColor = .red

//add white 2 pixel border outline
view.bezierPathBorder(.white, width: 2)

//remove border outline (optional)
view.removeBezierPathBorder()
13
Peter Kreinz

Pour une implémentation Swift, vous pouvez l'ajouter en tant qu'extension UIView.

extension UIView {

    struct Constants {
        static let ExternalBorderName = "externalBorder"
    }

    func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.whiteColor()) -> CALayer {
        let externalBorder = CALayer()
        externalBorder.frame = CGRectMake(-borderWidth, -borderWidth, frame.size.width + 2 * borderWidth, frame.size.height + 2 * borderWidth)
        externalBorder.borderColor = borderColor.CGColor
        externalBorder.borderWidth = borderWidth
        externalBorder.name = Constants.ExternalBorderName

        layer.insertSublayer(externalBorder, atIndex: 0)
        layer.masksToBounds = false

        return externalBorder
    }

    func removeExternalBorders() {
        layer.sublayers?.filter() { $0.name == Constants.ExternalBorderName }.forEach() {
            $0.removeFromSuperlayer()
        }
    }

    func removeExternalBorder(externalBorder: CALayer) {
        guard externalBorder.name == Constants.ExternalBorderName else { return }
        externalBorder.removeFromSuperlayer()
    }

}
9
picciano

Augmentez la largeur et la hauteur du cadre de la vue avec la largeur de la bordure avant d'ajouter la bordure:

float borderWidth = 2.0f
CGRect frame = self.frame;
frame.width += borderWidth;
frame.height += borderWidth;
 self.layer.borderColor = [UIColor yellowColor].CGColor;
 self.layer.borderWidth = 2.0f;
2
Hossam Ghareeb

Il y a en fait une solution très simple. Il suffit de les définir comme ceci:

view.layer.borderWidth = 5

view.layer.borderColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.5).cgColor

view.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.25).cgColor
1
Nikolas Ioannou

J'ai aimé la solution de @picciano & @Maksim Kniazev. Nous pouvons également créer une bordure annulaire avec les éléments suivants:

func addExternalAnnularBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) {
    let externalBorder = CALayer()
    externalBorder.frame = CGRect(x: -borderWidth*2, y: -borderWidth*2, width: frame.size.width + 4 * borderWidth, height: frame.size.height + 4 * borderWidth)
    externalBorder.borderColor = borderColor.cgColor
    externalBorder.borderWidth = borderWidth
    externalBorder.cornerRadius = (frame.size.width + 4 * borderWidth) / 2
    externalBorder.name = Constants.ExternalBorderName
    layer.insertSublayer(externalBorder, at: 0)
    layer.masksToBounds = false
}
0
Jimbung

J'ai aimé la solution de @picciano Si vous voulez un cercle explosif au lieu d'un carré, remplacez addExternalBorder function par:

func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) {
        let externalBorder = CALayer()
        externalBorder.frame = CGRect(x: -borderWidth, y: -borderWidth, width: frame.size.width + 2 * borderWidth, height: frame.size.height + 2 * borderWidth)
        externalBorder.borderColor = borderColor.cgColor
        externalBorder.borderWidth = borderWidth
        externalBorder.cornerRadius = (frame.size.width + 2 * borderWidth) / 2
        externalBorder.name = Constants.ExternalBorderName
        layer.insertSublayer(externalBorder, at: 0)
        layer.masksToBounds = false

    }
0
Maksim Kniazev