Fügen Sie einen Rahmen außerhalb eines UIView hinzu (anstatt innerhalb)

82

Wenn Sie einen Rahmen einer Ansicht mithilfe von Code in einer Ansicht wie hinzufügen

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

Der Rand wird in der Ansicht wie folgt hinzugefügt: Geben Sie hier die Bildbeschreibung ein

Die rechte Ansicht ist die Originalansicht. Wie Sie sehen können, ist der schwarze Bereich der Randansicht kleiner als der Originalbereich. aber was ich bekommen möchte, ist eine Grenze außerhalb der ursprünglichen Ansicht, wie folgt : Geben Sie hier die Bildbeschreibung ein. Der schwarze Bereich entspricht dem ursprünglichen Bereich. Wie kann ich ihn implementieren?

Remykits
quelle

Antworten:

100

Leider gibt es nicht nur eine kleine Eigenschaft, die Sie festlegen können, um den Rand nach außen auszurichten. Es wird nach innen ausgerichtet gezeichnet, da die Standardzeichnungsvorgänge von UIViews innerhalb seiner Grenzen zeichnen.

Die einfachste Lösung, die Ihnen in den Sinn kommt, besteht darin, die UIView beim Anwenden des Rahmens um die Größe der Rahmenbreite zu erweitern:

CGFloat borderWidth = 2.0f;

self.frame = CGRectInset(self.frame, -borderWidth, -borderWidth);
self.layer.borderColor = [UIColor yellowColor].CGColor;
self.layer.borderWidth = borderWidth;
Elliott James Perry
quelle
Ich habe dies aus Sicht der uitableviewcell verwendet. aber in der Zeile ist self.frame = CGRectInset (self.frame, -borderWidth, -borderWidth); Die Breite und Höhe der Ansicht nimmt weiter zu, wenn wir die Tabellenansicht weiter scrollen. Also habe ich sie entfernt.
Neela
Diese Methode erhöht die Ansichtshöhe weiter, sodass ich diesen Code entferne und den folgenden Code verwende: self.view.layer.borderColor = [[UIColor colorWithRed: 209.0f / 255.0f grün: 33.0f / 255.0f blau: 8.0f / 255.0f alpha: 1.0f] CGColor]; self.view.layer.borderWidth = 1.0f;
Rizwan Shah
4
Versuchte diese Lösung in Swift, funktioniert leider nicht, der Rand zeichnet immer wieder in der UIImageView
theDC
Befindet sich dieser Code in einer Methode, die mehrfach aufgerufen wird, nimmt seine Größe weiter zu, da sich self.frame wiederholt. Um dies zu verhindern, deklarieren Sie eine Eigenschaft zum Speichern von self.frame und legen Sie sie in viewDidload fest. Beispiel: _originalSize = self.frame; Dabei ist originalSize eine CGRect-Eigenschaft. Ändern Sie dann den Code in self.frame = CGRectInset (_originalSize, -borderWidth, -borderWidth);
GeneCode
Wenn dies in ViewDidLoad implementiert ist, wird die Größe der Ansicht automatisch auf die Bildschirmgröße angepasst (dh die Größe der Ansicht wird auf die ursprüngliche Größe geändert und somit wird der Rand sichtbar). Wenn es in ViewDidAppear implementiert ist, wird außerhalb der ursprünglichen Ansicht überhaupt kein Rahmen erstellt. Irgendwelche Gedanken darüber, wie es implementiert werden soll, damit der Rahmen außerhalb der ursprünglichen Ansicht erstellt wird UND die Größe der Ansicht nicht geändert wird?
JeffB6688
23

Ok, es gibt bereits eine akzeptierte Antwort, aber ich denke, es gibt einen besseren Weg, dies zu tun. Sie müssen nur eine neue Ebene haben, die etwas größer als Ihre Ansicht ist, und sie nicht bis an die Grenzen der Ebene der Ansicht maskieren (was tatsächlich der Fall ist) das Standardverhalten). Hier ist der Beispielcode:

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;

Dies ist natürlich der Fall, wenn Sie möchten, dass Ihr Rand 1 Einheit groß ist. Wenn Sie mehr möchten, passen Sie den borderWidthund den Rahmen der Ebene entsprechend an. Dies ist besser als die Verwendung einer zweiten Ansicht, die etwas größer ist, da a CALayerleichter als a ist UIViewund Sie den Rahmen von nicht ändern müssen. Dies ist myViewbeispielsweise dann gut, wenn myViewes sich um a handeltUIImageView

NB: Für mich war das Ergebnis im Simulator nicht perfekt (die Schicht war nicht genau an der richtigen Position, so dass die Schicht manchmal auf einer Seite dicker war), aber genau das, was auf einem realen Gerät verlangt wird.

BEARBEITEN

Eigentlich war das Problem, über das ich in der NB spreche, nur, weil ich den Bildschirm des Simulators verkleinert hatte. Bei normaler Größe gibt es absolut kein Problem

Ich hoffe es hilft

Hugues Duvillier
quelle
2
myView.layer.masksToBounds = NO; Es verursacht ein Problem, wenn myView über CornerRadius verfügt.
Umakanta
22

Mit der oben akzeptierten besten Antwort habe ich Erfahrungen mit so nicht schönen Ergebnissen und unschönen Kanten gemacht:

Rand ohne Bezierpfad

Also werde ich meine UIView Swift- Erweiterung mit Ihnen teilen, die stattdessen einen UIBezierPath als Rahmenumriss verwendet - ohne unschöne Kanten (inspiriert von @Fattie ):

Rand mit Bezierpfad

//  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()
    }

}

Beispiel:

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()
Peter Kreinz
quelle
in meinem Fall sollte es drinnen sein :)
Peter Kreinz
Gute Denkweise, aber Ihre Grenze ist immer noch "innen", daher muss dies behoben werden, um einen Umriss zu erhalten :)
Serj Rubens
15

Bei einer Swift-Implementierung können Sie diese als UIView-Erweiterung hinzufügen.

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()
    }

}
Picciano
quelle
Ausgezeichnet! Danke dir! Einige Dinge wurden in Swift 4 geändert und sehen jetzt anders aus, aber Xcode erklärt, wie Code geändert wird. Und in der Methode 'removeExternalBorder' muss 'guard externalBorder.name ==' sein.
DmitryKanunnikoff
13

Nun, es gibt keine direkte Methode, um dies zu tun. Sie können einige Problemumgehungen in Betracht ziehen.

  1. Ändern und vergrößern Sie den Rahmen und fügen Sie wie Sie eine Rahmenfarbe hinzu
  2. Fügen Sie eine Ansicht hinter der aktuellen Ansicht mit der größeren Größe hinzu, sodass sie als Rahmen angezeigt wird. Kann als benutzerdefinierte Ansichtsklasse verwendet werden
  3. Wenn Sie keinen bestimmten Rand benötigen (eindeutiger Rand), können Sie sich für diesen Zweck auf den Schatten verlassen

    [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;
Lithu TV
quelle
2

Erhöhen Sie die Breite und Höhe des Rahmens der Ansicht mit der Rahmenbreite, bevor Sie den Rahmen hinzufügen:

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;
Hossam Ghareeb
quelle
2

Es gibt tatsächlich eine sehr einfache Lösung. Stellen Sie beide einfach so ein:

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

quelle
1

Ich mochte die Lösung von @picciano Wenn Sie einen explodierenden Kreis anstelle eines Quadrats möchten, ersetzen Sie die Funktion addExternalBorder durch:

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

    }
Maksim Kniazev
quelle
0

Ich mochte die Lösung von @picciano & @Maksim Kniazev. Wir können auch einen ringförmigen Rand mit folgendem erstellen:

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
}
Jimbung
quelle
0

Wenn ich im Storyboard einen Rahmen um meine UI-Ansicht (main - SubscriptionAd) platziere, platziere ich ihn in einer anderen UI-Ansicht (background - BackgroundAd). Die Hintergrund-UIView hat eine Hintergrundfarbe, die der gewünschten Rahmenfarbe entspricht, und die Haupt-UIView hat von jeder Seite den Einschränkungswert 2.

Ich werde die Hintergrundansicht mit meinem ViewController verknüpfen und dann den Rahmen durch Ändern der Hintergrundfarbe ein- und ausschalten.

Bild der verschachtelten UIView mit 2px-Einschränkungen für die Draufsicht, wodurch sie kleiner als größer wird

JonesJr876
quelle
0

Swift 5

extension UIView {
    fileprivate struct Constants {
        static let externalBorderName = "externalBorder"
    }

    func addExternalBorder(borderWidth: CGFloat = 2.0, borderColor: UIColor = UIColor.white) -> CALayer {
        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.name = Constants.ExternalBorderName

        layer.insertSublayer(externalBorder, at: 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()
    }
}
Pacyjent
quelle