programing

칼레이어를 사용한 반올림된 UI 보기 - 일부 코너만 - 어떻게?

abcjava 2023. 5. 1. 19:49
반응형

칼레이어를 사용한 반올림된 UI 보기 - 일부 코너만 - 어떻게?

내 응용 프로그램에는 다음과 같은 이름의 네 개의 버튼이 있습니다.

  • 위 - 왼쪽
  • 아래 - 왼쪽
  • 오른쪽 위
  • 오른쪽 아래

버튼 위에는 영상 보기(또는 UI 보기)가 있습니다.

이제 사용자가 왼쪽 위 버튼을 탭한다고 가정해 보겠습니다.위의 이미지/보기는 해당 특정 모서리에서 반올림해야 합니다.

UIView에 둥근 모서리를 적용하는 데 어려움을 겪고 있습니다.

현재 다음 코드를 사용하여 둥근 모서리를 각 보기에 적용하고 있습니다.

    // imgVUserImg is a image view on IB.
    imgVUserImg.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"any Url Here"];
    CALayer *l = [imgVUserImg layer];
    [l setMasksToBounds:YES];
    [l setCornerRadius:5.0];  
    [l setBorderWidth:2.0];
    [l setBorderColor:[[UIColor darkGrayColor] CGColor]];

위의 코드는 제공된 뷰의 각 모서리에 라운드니스를 적용하는 것입니다.대신에 저는 상단 / 상단 + 왼쪽 / 하단 + 오른쪽 등과 같은 선택된 모서리에 라운드를 적용하고 싶었습니다.

가능할까요? 어떻게요?

3부터는 iOS 3.2의 할 수 .UIBezierPaths - 즉시 사용할 수 있는 반올림된 직사각형(지정한 모서리만 반올림됨)을 만듭니다.그런 다음 이것을 다음 경로로 사용할 수 있습니다.CAShapeLayer이를 뷰 도면층의 마스크로 사용합니다.

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds 
                                               byRoundingCorners:UIRectCornerTopLeft
                                                     cornerRadii:CGSizeMake(10.0, 10.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageView.bounds;
maskLayer.path = maskPath.CGPath;

// Set the newly created shape layer as the mask for the image view's layer
imageView.layer.mask = maskLayer;

그리고 그것이 바로 코어 그래픽에서 도형을 수동으로 정의하는 데 방해가 되지 않으며 포토샵에서 마스킹 이미지를 만들지 않습니다.계층은 무효화할 필요도 없습니다.둥근 모서리를 적용하거나 새 모서리로 변경하는 것은 새 모서리를 정의하는 것만큼 간단합니다.UIBezierPath그리고 그것을 사용합니다.CGPath마스크 계층의 경로로 사용됩니다.corners의 파라미터bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:메소드는 비트 마스크이므로 OR링을 통해 여러 모서리를 반올림할 수 있습니다.


편집: 그림자 추가

여기에 그림자를 추가하려면 작업이 조금 더 필요합니다.

하면냐왜" 때문입니다.imageView.layer.mask = maskLayer마스크를 적용하면 일반적으로 마스크 외부에 그림자가 표시되지 않습니다.뷰를 두개의 레이어를 입니다.CALayer으로: 뷰의도으로층:shadowLayer그리고.roundedLayer 다 ▁of를 사용해야 합니다.UIBezierPath는 이지가다추가다니됩의 됩니다.roundedLayer.

// Create a transparent view
UIView *theView = [[UIView alloc] initWithFrame:theFrame];
[theView setBackgroundColor:[UIColor clearColor]];

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:theView.bounds 
                                               byRoundingCorners:UIRectCornerTopLeft
                                                     cornerRadii:CGSizeMake(10.0f, 10.0f)];

// Create the shadow layer
CAShapeLayer *shadowLayer = [CAShapeLayer layer];
[shadowLayer setFrame:theView.bounds];
[shadowLayer setMasksToBounds:NO];
[shadowLayer setShadowPath:maskPath.CGPath];
// ...
// Set the shadowColor, shadowOffset, shadowOpacity & shadowRadius as required
// ...

// Create the rounded layer, and mask it using the rounded mask layer
CALayer *roundedLayer = [CALayer layer];
[roundedLayer setFrame:theView.bounds];
[roundedLayer setContents:(id)theImage.CGImage];

CAShapeLayer *maskLayer = [CAShapeLayer layer];
[maskLayer setFrame:theView.bounds];
[maskLayer setPath:maskPath.CGPath];

roundedLayer.mask = maskLayer;

// Add these two layers as sublayers to the view
[theView.layer addSublayer:shadowLayer];
[theView.layer addSublayer:roundedLayer];

저는 아이폰에서 어떻게 둥근 모서리의 UI 레이블을 만들 수 있습니까? 그리고 아이폰에서 투명도가 있는 둥근 직선 뷰는 어떻게 됩니까?에서 답변을 사용했습니다.이 코드를 만드는 것.

그런 다음 잘못된 질문에 답했다는 것을 깨닫고(UIImage 대신 둥근 UI 레이블을 지정) 이 코드를 사용하여 변경했습니다.

http://discussions.apple.com/thread.jspa?threadID=1683876

View 템플릿으로 iPhone 프로젝트를 만듭니다.보기 컨트롤러에서 다음을 추가합니다.

- (void)viewDidLoad
{
    CGRect rect = CGRectMake(10, 10, 200, 100);
    MyView *myView = [[MyView alloc] initWithFrame:rect];
    [self.view addSubview:myView];
    [super viewDidLoad];
}

MyView 냥그입니다.UIImageView하위 클래스:

@interface MyView : UIImageView
{
}

저는 그래픽 콘텍스트를 사용해 본 적이 없지만, 이 코드를 간신히 연결할 수 있었습니다.모서리 두 개에 대한 코드가 누락되었습니다. 제가 이걸 구현했는지 알 수 .CGContextAddArc호출 및 코드에서 반지름 값의 일부를 삭제합니다.모든 코너의 코드는 거기에 있으므로 이 코드를 시작점으로 사용하고 필요 없는 코너를 만드는 부분은 삭제하십시오.원하는 경우 두 개 또는 세 개의 둥근 모서리가 있는 직사각형도 만들 수 있습니다.

코드가 완벽하지는 않지만, 당신이 그것을 조금 정리할 수 있다고 확신합니다.

static void addRoundedRectToPath(CGContextRef context, CGRect rect, float radius, int roundedCornerPosition)
{

    // all corners rounded
    //  CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
    //  CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
    //  CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, 
    //                  radius, M_PI / 4, M_PI / 2, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius, 
    //                          rect.origin.y + rect.size.height);
    //  CGContextAddArc(context, rect.origin.x + rect.size.width - radius, 
    //                  rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
    //  CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, 
    //                  radius, 0.0f, -M_PI / 2, 1);
    //  CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
    //  CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, 
    //                  -M_PI / 2, M_PI, 1);

    // top left
    if (roundedCornerPosition == 1) {
        CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
        CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, 
                        radius, M_PI / 4, M_PI / 2, 1);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, 
                                rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y);
    }   

    // bottom left
    if (roundedCornerPosition == 2) {
        CGContextMoveToPoint(context, rect.origin.x, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, 
                                rect.origin.y + rect.size.height);
        CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
        CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
        CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, 
                        -M_PI / 2, M_PI, 1);
    }

    // add the other corners here


    CGContextClosePath(context);
    CGContextRestoreGState(context);
}


-(UIImage *)setImage
{
    UIImage *img = [UIImage imageNamed:@"my_image.png"];
    int w = img.size.width;
    int h = img.size.height;

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);

    CGContextBeginPath(context);
    CGRect rect = CGRectMake(0, 0, w, h);


    addRoundedRectToPath(context, rect, 50, 1);
    CGContextClosePath(context);
    CGContextClip(context);

    CGContextDrawImage(context, rect, img.CGImage);

    CGImageRef imageMasked = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    [img release];

    return [UIImage imageWithCGImage:imageMasked];
}

alt 텍스트 http://nevan.net/skitch/skitched-20100224-092237.png

이 작업을 수행하려면 QuartzCore 프레임워크가 필요합니다.

나는 이 코드를 내 코드의 여러 곳에서 사용해왔고 100% 올바르게 작동합니다.하나의 특성을 "원형 코너별"로 변경하여 임의의 코더를 변경할 수 있습니다.UIRect 코너 왼쪽 하단"

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerBottomLeft cornerRadii:CGSizeMake(10.0, 10.0)];

                CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
                maskLayer.frame = view.bounds;
                maskLayer.path = maskPath.CGPath;
                view.layer.mask = maskLayer;
                [maskLayer release];

iOS 11에서는 이제 일부 코너만 라운드할 수 있습니다.

let view = UIView()

view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]

Swift 3+ 구문을 사용하는 CAL 계층 확장

extension CALayer {

    func round(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize) -> Void {
        let bp = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: cornerRadii)
        let sl = CAShapeLayer()
        sl.frame = self.bounds
        sl.path = bp.cgPath
        self.mask = sl
    }
}

다음과 같이 사용할 수 있습니다.

let layer: CALayer = yourView.layer
layer.round(roundedRect: yourView.bounds, byRoundingCorners: [.bottomLeft, .topLeft], cornerRadii: CGSize(width: 5, height: 5))

특정 모서리를 반올림하는 스터트 예제는 유용합니다.여러 모서리를 왼쪽 위와 오른쪽 위처럼 반올림하려면 이 방법을 사용합니다.

// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageview
                                               byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight
                                                     cornerRadii:CGSizeMake(10.0, 10.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageview.bounds;
maskLayer.path = maskPath.CGPath;

// Set the newly created shape layer as the mask for the image view's layer
imageview.layer.mask = maskLayer; 

필요에 따라 작동할 수 있고 그림자에도 작동할 수 있는 더 쉽고 빠른 대답이 있습니다.수퍼레이어에서 maskToBounds를 true로 설정하고 하위 레이어의 모서리 2개가 수퍼레이어 경계 밖에 있도록 오프셋하여 두 변의 둥근 모서리를 효과적으로 잘라낼 수 있습니다.

물론 이것은 같은 면에 두 개의 둥근 모서리만 있고 한 면에서 몇 개의 픽셀을 잘라낼 때 레이어의 내용이 동일하게 보일 때만 작동합니다.막대 차트를 위쪽에서만 반올림할 수 있습니다.

공유해 주셔서 감사합니다.이 문제에 대한 추가 참조를 위해 swift 2.0에 대한 솔루션을 공유하고자 합니다.(UIRectCorner의 프로토콜을 준수

let mp = UIBezierPath(roundedRect: cell.bounds, byRoundingCorners: [.bottomLeft, .TopLeft], cornerRadii: CGSize(width: 10, height: 10))
let ml = CAShapeLayer()
ml.frame = self.bounds
ml.path = mp.CGPath
self.layer.mask = ml

관련 질문을 참조하십시오.당신은 당신 자신의 직사각형을 그려야 할 것입니다.CGPath약간의 둥근 모서리와 함께, 추가합니다.CGPath당신에게CGContext다음을 사용하여 클립으로 고정합니다.CGContextClip.

또한 알파 값을 사용하여 반올림한 직선을 이미지에 그린 다음 해당 이미지를 사용하여 레이어 값으로 설정한 새 레이어를 만들 수 있습니다.mask속성(Apple 설명서 참조).

5년 후의 일이지만, 저는 사람들이 이것을 하는 현재의 방식이 100% 옳지 않다고 생각합니다.많은 사람들이 UIBizerPath + CASapeLayer 메서드를 사용하면 자동 레이아웃이 특히 스토리보드에 설정되어 있을 때 자동 레이아웃을 방해한다는 문제를 가지고 있습니다.이 문제에 대한 답은 없습니다. 그래서 저는 제 것을 추가하기로 결정했습니다.

이를 피하기 위한 매우 쉬운 방법이 있습니다. 즉, 모서리의 둥근 부분을 그립니다.drawRect(rect: CGRect)기능.

예를 들어, UIView의 모서리를 반올림하려면 UIView를 하위 클래스로 분류한 다음 해당 하위 클래스를 사용합니다.

import UIKit

class TopRoundedView: UIView {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        var maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: UIRectCorner.TopLeft | UIRectCorner.TopRight, cornerRadii: CGSizeMake(5.0, 5.0))

        var maskLayer = CAShapeLayer()
        maskLayer.frame = self.bounds
        maskLayer.path = maskPath.CGPath

        self.layer.mask = maskLayer
    }
}

이것은 문제를 극복하는 가장 좋은 방법이며 적응하는 데 전혀 시간이 걸리지 않습니다.

자동 크기 조정 또는 자동 레이아웃에서는 일부 모서리만 반올림하면 제대로 재생되지 않습니다.

그래서 또 다른 옵션은 일반적으로 사용하는 것입니다.cornerRadius다른 뷰 아래 또는 수퍼뷰 경계 밖에서 원하지 않는 모서리를 숨겨 콘텐츠를 클리핑하도록 설정합니다.

답변추가 사항에 추가하기 위해 단순하고 재사용 가능한 솔루션을 만들었습니다.UIView사례에 것을)하고 , 하고 싶었습니다.사용 사례에 따라 수정(모든 레이아웃에 개체를 만들지 않음)할 수도 있지만 가능한 한 단순하게 유지하고 싶었습니다.하면 다른(예: 확사면다예보른기하용장에 이 을 적용할 수 .UIImageView하위 분류를 좋아하지 않는 경우 더 쉽습니다.

extension UIView {

    func roundCorners(_ roundedCorners: UIRectCorner, toRadius radius: CGFloat) {
        roundCorners(roundedCorners, toRadii: CGSize(width: radius, height: radius))
    }

    func roundCorners(_ roundedCorners: UIRectCorner, toRadii cornerRadii: CGSize) {
        let maskBezierPath = UIBezierPath(
            roundedRect: bounds,
            byRoundingCorners: roundedCorners,
            cornerRadii: cornerRadii)
        let maskShapeLayer = CAShapeLayer()
        maskShapeLayer.frame = bounds
        maskShapeLayer.path = maskBezierPath.cgPath
        layer.mask = maskShapeLayer
    }
}

class RoundedCornerView: UIView {

    var roundedCorners: UIRectCorner = UIRectCorner.allCorners
    var roundedCornerRadii: CGSize = CGSize(width: 10.0, height: 10.0)

    override func layoutSubviews() {
        super.layoutSubviews()
        roundCorners(roundedCorners, toRadii: roundedCornerRadii)
    }
}

다음은 에 적용하는 방법입니다.UIViewController:

class MyViewController: UIViewController {

    private var _view: RoundedCornerView {
        return view as! RoundedCornerView
    }

    override func loadView() {
        view = RoundedCornerView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        _view.roundedCorners = [.topLeft, .topRight]
        _view.roundedCornerRadii = CGSize(width: 10.0, height: 10.0)
    }
}

스튜어트의 답변을 마무리하면 다음과 같은 반올림 코너 방법을 사용할 수 있습니다.

@implementation UIView (RoundCorners)

- (void)applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];

    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;

    self.layer.mask = maskLayer;
}

@end

따라서 반올림 모서리를 적용하려면 다음 작업을 수행합니다.

[self.imageView applyRoundCorners:UIRectCornerTopRight|UIRectCornerTopLeft radius:10];

레이어의 마스크를 정의하는 것이 좋습니다.마스크 자체는 다음과 같아야 합니다.CAShapeLayer전용 경로가 있는 개체입니다.다음 UIView 확장(Swift 4.2)을 사용할 수 있습니다.

extension UIView {
    func round(corners: UIRectCorner, with radius: CGFloat) {
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = UIBezierPath(
            roundedRect: bounds,
            byRoundingCorners: corners,
            cornerRadii: CGSize(width: radius, height: radius)
        ).cgPath
        layer.mask = maskLayer
   }
}

언급URL : https://stackoverflow.com/questions/2264083/rounded-uiview-using-calayers-only-some-corners-how

반응형