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); tableview를 계속 스크롤하면 view의 너비와 높이가 계속 증가합니다. 그래서 저는 그것을 제거하여 끝났습니다.
Neela

이 방법은 뷰 높이를 계속 늘려서이 코드를 제거하고 다음 코드를 사용합니다. 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;
Rizwan Shah 2015

4
Swift에서이 솔루션을 시도했지만 안타깝게도 작동하지 않습니다. 테두리가 UIImageView 내부에 계속 그려져 있습니다
theDC

이 코드가 여러 번 호출되는 메서드 내부에 있으면 self.frame이 반복되기 때문에 크기가 계속 증가합니다. 이를 방지하려면 self.frame을 저장할 속성을 선언하고 viewDidload에 설정합니다. 예 : _originalSize = self.frame; 여기서 originalSize는 CGRect 속성입니다. 그런 다음 코드를 self.frame = CGRectInset (_originalSize, -borderWidth, -borderWidth);로 변경합니다.
GeneCode

이것이 ViewDidLoad에서 구현되면 뷰는 화면 크기로 자동 조정됩니다 (즉, 뷰가 원래 크기로 조정되어 테두리가 표시됨). ViewDidAppear에서 구현 된 경우 원래 뷰 외부에 테두리가 전혀 생성되지 않습니다. 테두리가 원래 뷰 외부에 생성되고 뷰의 크기가 조정되지 않도록 구현하는 방법에 대한 생각이 있으십니까?
JeffB6688

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그에 따라 레이어의 프레임을 조정하는 것 입니다. 이것은 더 나은 두 번째보기 a를 사용하는 것보다이 같이 큰 비트입니다 CALayer(A)보다 가볍다 UIView그리고 당신의 프레임 수정 할 수없는 myView경우 예를 들어 좋은이며, myViewA는UIImageView

NB : 저에게 결과는 시뮬레이터에서 완벽하지 않았습니다 (레이어가 정확히 올바른 위치에 있지 않아서 가끔 레이어가 한쪽면이 더 두꺼 웠습니다). 실제 장치에서 정확히 요구하는 내용이었습니다.

편집하다

사실 NB 에서 제가 이야기하는 문제 는 시뮬레이터의 화면을 줄 였기 때문에 정상적인 크기에서는 전혀 문제가 없습니다

도움이되기를 바랍니다.


2
myView.layer.masksToBounds = 아니요; myView에 CornerRadius가 있으면 문제가 발생합니다.
umakanta 2017-06-29

.masksToBounds = no // 스케일링 모드가 aspectFill 인 경우 이미지가 오버 플로우됩니다.
iOS Blacksmith

22

위의 수락 된 베스트 답변으로 나는 그러한 경험을했습니다. 좋지 않은 결과와보기 흉한 가장자리를 .

베 지어 경로가없는 테두리

따라서 보기 흉한 가장자리없이 UIBezierPath를 테두리 윤곽선으로 사용하는 UIView Swift 확장 기능을 공유 하겠습니다 ( @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()

이 : 내부해야 내 경우
피터 Kreinz

좋은 생각이지만 테두리는 여전히 "내부"이므로 윤곽선이되도록 수정해야합니다. :)
Serj Rubens

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

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 & @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

Storyboard에서 내 UI 뷰 (기본-SubscriptionAd) 주위에 테두리를 배치하는 방법은 다른 UI 뷰 (배경-BackgroundAd) 내에 배치하는 것입니다. Background UIView에는 내가 원하는 테두리 색상과 일치하는 배경색이 있고 Main UIView에는 각 측면에서 제약 값 2가 있습니다.

배경 뷰를 ViewController에 연결 한 다음 배경색을 변경하여 테두리를 켜고 끕니다.

Top View 2px 제약 조건이있는 중첩 된 UIView 이미지로, 더 작게 만듭니다.


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.