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


82

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

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

境界線は、次のようにビュー内に追加されます。 ここに画像の説明を入力してください

右側のビューは元のビューです。ご覧のとおり、境界線のビューの黒い領域は元のビューよりも小さくなっています。しかし、私が取得したいのは、次のように、元のビューの外側の境界線ですここに画像の説明を入力してください。黒い領域は元の領域と同じですが、どうすれば実装できますか?

回答:


100

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

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

CGFloat borderWidth = 2.0f;

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

私はこれをuitableviewcellのビューで使用しました。しかし、その行では、self.frame = CGRectInset(self.frame、-borderWidth、-borderWidth); テーブルビューをスクロールし続けると、ビューの幅と高さが増え続けます。それで、私はそれを削除することになりました。
ニーラ2015年

このメソッドはビューの高さを増やし続けるので、このコードを削除して次のコードを使用しますself.view.layer.borderColor = [[UIColor colorWithRed:209.0f / 255.0f green:33.0f / 255.0f blue:8.0f / 255.0f alpha: 1.0f] CGColor]; self.view.layer.borderWidth = 1.0f;
リズワンシャー2015

4
Swiftでこのソリューションを試しましたが、残念ながら機能しません。境界線はUIImageView内に描画され続けます
theDC

このコードが複数回呼び出されるメソッド内にある場合、self.frameはそれ自体を繰り返すため、サイズが大きくなり続けます。これを防ぐには、self.frameを格納するプロパティを宣言し、viewDidloadに設定します。たとえば、_originalSize = self.frame; ここで、originalSizeはCGRectプロパティです。次に、コードをself.frame = CGRectInset(_originalSize、-borderWidth、-borderWidth);に変更します。
GeneCode 2016

これがViewDidLoadに実装されると、ビューは画面サイズに自動的にサイズ変更されます(つまり、ビューは元のサイズにサイズ変更されるため、境界線が表示されます)。ViewDidAppearに実装されている場合、元のビューの外側に境界線はまったく作成されません。境界線が元のビューの外側に作成され、ビューのサイズが変更されないように実装する方法について何か考えはありますか?
JeffB6688 2016

23

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

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は、それに応じてレイヤーのフレームを調整します。これは、aCALayerがaよりも軽くUIView、のフレームを変更する必要がないため、少し大きい2番目のビューを使用するよりもmyView優れています。これmyViewは、たとえば、UIImageView

注意:私にとって、結果はシミュレーターでは完璧ではありませんでした(レイヤーが正確に正しい位置になかったため、レイヤーの片側が厚くなることがありました)が、実際のデバイスで求められていたものでした。

編集

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

それが役に立てば幸い


2
myView.layer.masksToBounds = NO; myViewにCornerRadiusがある場合、問題が発生します。
umakanta 2017年

.masksToBounds = no //スケーリングモードがaspectFillの場合、これも画像をオーバーフローさせます
iOS Blacksmith

22

上記の受け入れられたベストアンサーで、私はそのような良くない結果と見苦しいエッジで経験をしました:

ベジェパスのない境界

そこで、UIView Swift拡張機能を共有します。これは、代わりにUIBezierPathを境界線のアウトラインとして使用します。見苦しいエッジはありません@Fattieに触発されています)。

ベジェパスとの境界

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

私の場合、それは中にあるべきです:)
Peter Kreinz 2018

良い考え方ですが、境界線はまだ「内側」にあるため、輪郭を
描く

15

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

}

優秀な!ありがとうございました!Swift 4で変更され、外観が異なるものもありますが、Xcodeではコードの変更方法について説明しています。また、メソッド 'removeExternalBorder'は 'guard externalBorder.name =='でなければなりません。
DmitryKanunnikoff 2017

13

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

  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;
    

2

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

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

実際には非常に単純な解決策があります。両方を次のように設定するだけです。

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

@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

@piccianoと@MaksimKniazevのソリューションが気に入りました。次の方法で環状の境界線を作成することもできます。

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

StoryboardでUIビュー(メイン-SubscriptionAd)の周囲に境界線を配置する方法は、別のUIビュー(背景-BackgroundAd)内に境界線を配置することです。背景UIViewには、必要な境界線の色と一致する背景色があり、メインUIViewには、両側からの制約値2があります。

背景ビューをViewControllerにリンクしてから、背景色を変更して境界線のオンとオフを切り替えます。

ネストされたUIViewの画像とトップビュー2pxの制約があり、大きくなるよりも小さくなります


0

スウィフト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()
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.