94 votes

Ajouter une bordure à l'extérieur d'une UIView (au lieu de l'intérieur)

Si l'on ajoute une bordure à une vue en utilisant un code dans une vue comme

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 originale, comme vous pouvez le voir, la zone noire de la vue bordée est inférieure à celle de la vue originale. mais ce que je veux obtenir est une bordure en dehors de la vue originale, comme ceci : enter image description here . la zone noire est égale à la zone originale, comment puis-je l'implémenter ?

107voto

Elliott Perry Points 2172

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

La solution la plus simple qui me vient à l'esprit serait d'étendre l'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;

26voto

picciano Points 5638

Pour une mise en œuvre en 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()
    }

}

26voto

screenworker Points 31

Avec la meilleure réponse acceptée ci-dessus, j'ai fait des expériences de ce type pas sympa résultats et des bords inesthétiques :

border without bezier path

Je vais donc partager mon UIView Swift avec vous, qui utilise un UIBezierPath à la place comme contour de la frontière - sans bords disgracieux (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()

25voto

Hugues Duvillier Points 487

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, vous devez juste avoir un nouveau calque un peu plus grand que votre vue et ne pas le masquer aux limites du calque de la vue (ce 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, c'est si vous voulez que votre bordure soit de 1 unité de large, si vous en voulez plus, vous adaptez l'option borderWidth et le cadre de la couche en conséquence. C'est mieux que d'utiliser une deuxième vue un peu plus grande comme une CALayer est plus léger qu'un UIView et vous n'avez pas besoin de modifier le cadre de la myView ce qui est une bonne chose, par exemple si myView est un UIImageView

N.B : Pour moi, le résultat n'était pas parfait sur le simulateur (la couche n'était pas exactement à la bonne position, de sorte que la couche était parfois plus épaisse d'un côté) mais correspondait exactement à ce qui est demandé sur le dispositif réel.

EDIT

En fait, le problème dont je parle dans le N.B. c'est juste 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 cela vous aidera

13voto

Lithu T.V Points 10117

Il n'y a pas de méthode directe pour le faire Vous pouvez envisager des solutions de contournement.

  1. Modifiez et augmentez le cadre et ajoutez le bordercolor comme vous l'avez fait.
  2. Ajoute une vue derrière la vue actuelle avec la plus grande taille de sorte qu'elle apparaisse comme une bordure.
  3. Si vous n'avez pas besoin d'une frontière définie (frontière nette), vous pouvez compter sur l'ombre pour le faire.

    [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;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X