web-dev-qa-db-ja.com

(内側ではなく)UIViewの外側に境界線を追加します

次のようなビューでコードを使用してビューの境界線を追加する場合

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

次のようにビュー内に境界線が追加されます。 enter image description here

ご覧のとおり、右側のビューは元のビューです。境界線付きビューの黒い領域は元のビューよりも小さくなっています。しかし、私が取得したいのは、次のような元のビューの外側の境界線です:enter image description here。黒い領域は元の領域と同じですが、どうすれば実装できますか?

77
remykits

残念ながら、境界線を外側に揃えるために設定できる小さなプロパティはありません。 UIViewsのデフォルトの描画操作はその境界内で描画するため、内側に揃えて描画します。

頭に浮かぶ最も簡単な解決策は、境界線を適用するときに、境界線の幅のサイズでUIViewを拡張することです。

CGFloat borderWidth = 2.0f;

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

はい、すでに受け入れられた答えがありますが、それを行うためのより良い方法があると思います、あなたはあなたのビューより少し大きい新しいレイヤーを持っている必要があり、ビューのレイヤーの境界にマスクしないでください(実際にはデフォルトの動作)。サンプルコードは次のとおりです。

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;

もちろん、これは境界線を1単位の大きさにしたい場合です。もっと大きくしたい場合は、borderWidthとレイヤーのフレームをそれに応じて調整します。これは、CALayerUIViewよりも軽いため、2番目のビューを使用するよりも優れており、myViewのフレームを変更する必要はありません。 myViewはaUIImageViewです

N.B:私にとって、結果はシミュレーターでは完璧ではありませんでした(レイヤーが正確な位置になかったため、レイヤーが一方の側で厚くなったことがありました)が、実際のデバイスで要求されたとおりでした。

編集

実際にN.Bで話している問題は、シミュレーターの画面を縮小したからでした。通常のサイズではまったく問題はありません。

それが役に立てば幸い

20

上記の受け入れられた最良の答えで、私はそのようなではないニースの結果と見苦しいエッジを経験しました:

border without bezier path

したがって、UIViewSwift拡張機能を共有します。これは、UIBezierPathを境界線のアウトラインとして使用します-見苦しいエッジなし( @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()
    }

}

例:

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()
17
Peter Kreinz

直接的な方法はありません。いくつかの回避策を検討できます。

  1. フレームを変更および増加し、ボーダーカラーを追加しました
  2. ビューを境界線として表示されるように、現在のビューの後ろに大きいサイズで追加します。ビューのカスタムクラスとして機能できます。
  3. 明確な境界線(明確な境界線)が必要ない場合は、目的のために影に頼ることができます

    [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

Swift実装の場合、これを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()
    }

}
12
picciano

枠を追加する前に、枠の幅でビューのフレームの幅と高さを増やします。

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

実際には非常に簡単な解決策があります。両方を次のように設定します。

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

@piccianoと@Maksim Kniazevのソリューションが気に入りました。また、次のようにして環状の境界線を作成することもできます。

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

@ piccianoの解が好きでした。正方形の代わりに円を分解したい場合は、addExternalBorder関数を次のように置き換えます。

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