programing

UIView에서 두 모서리를 둥글게 만듭니다.

newsource 2023. 8. 23. 21:49

UIView에서 두 모서리를 둥글게 만듭니다.

조금 전에 뷰의 모서리만 반올림하는 것에 대한 질문을 올렸고, 좋은 반응을 얻었지만, 구현하는 데 어려움을 겪고 있습니다.다음은 내 그림 Rect: 메서드입니다.

- (void)drawRect:(CGRect)rect {
    //[super drawRect:rect]; <------Should I uncomment this?
    int radius = 5;
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextBeginPath(context);
    CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI / 2, 1);
    CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
    CGContextClosePath(context);
    CGContextClip(context);
}

메서드가 호출되지만 보기 결과에 영향을 미치지 않는 것 같습니다.왜 그런지 아십니까?

iOS 11에 도입된 CACnerMask는 보기 계층에서 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래를 정의하는 데 도움이 됩니다.다음은 사용 예입니다.

여기서는 두 개의 맨 위 모서리만 반올림합니다.

myView.clipsToBounds = true
myView.layer.cornerRadius = 10
myView.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]

참고:


제가 알기로는, 만약 당신이 또한 하위 뷰를 가릴 필요가 있다면, 당신은 사용할 수 있습니다.CALayer두 가지 방법이 있습니다.첫 번째 것은 조금 더 우아하고, 두 번째 것은 해결책입니다 :-) 하지만 속도도 빠릅니다.둘 다 다음을 기반으로 합니다.CALayerㅠㅠㅠㅠ 에서 두 을 다 것을 .저는 작년에 두 가지 프로젝트에서 두 가지 방법을 모두 사용했는데 당신이 유용한 것을 찾을 수 있기를 바랍니다.

솔루션 1

저는 생성하기 위해 이 .UIImage 제가 필요로 하는 둥근 모서리와 함께.이 기능에는 기본적으로 영상의 경계와 4개의 코너 반경(상단-좌단, 상단-우단, 하단-좌단 및 하단-우단)의 5개 파라미터가 필요합니다.


static inline UIImage* MTDContextCreateRoundedMask( CGRect rect, CGFloat radius_tl, CGFloat radius_tr, CGFloat radius_bl, CGFloat radius_br ) {  

    CGContextRef context;
    CGColorSpaceRef colorSpace;

    colorSpace = CGColorSpaceCreateDeviceRGB();

    // create a bitmap graphics context the size of the image
    context = CGBitmapContextCreate( NULL, rect.size.width, rect.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast );

    // free the rgb colorspace
    CGColorSpaceRelease(colorSpace);    

    if ( context == NULL ) {
        return NULL;
    }

    // cerate mask

    CGFloat minx = CGRectGetMinX( rect ), midx = CGRectGetMidX( rect ), maxx = CGRectGetMaxX( rect );
    CGFloat miny = CGRectGetMinY( rect ), midy = CGRectGetMidY( rect ), maxy = CGRectGetMaxY( rect );

    CGContextBeginPath( context );
    CGContextSetGrayFillColor( context, 1.0, 0.0 );
    CGContextAddRect( context, rect );
    CGContextClosePath( context );
    CGContextDrawPath( context, kCGPathFill );

    CGContextSetGrayFillColor( context, 1.0, 1.0 );
    CGContextBeginPath( context );
    CGContextMoveToPoint( context, minx, midy );
    CGContextAddArcToPoint( context, minx, miny, midx, miny, radius_bl );
    CGContextAddArcToPoint( context, maxx, miny, maxx, midy, radius_br );
    CGContextAddArcToPoint( context, maxx, maxy, midx, maxy, radius_tr );
    CGContextAddArcToPoint( context, minx, maxy, minx, midy, radius_tl );
    CGContextClosePath( context );
    CGContextDrawPath( context, kCGPathFill );

    // Create CGImageRef of the main view bitmap content, and then
    // release that bitmap context
    CGImageRef bitmapContext = CGBitmapContextCreateImage( context );
    CGContextRelease( context );

    // convert the finished resized image to a UIImage 
    UIImage *theImage = [UIImage imageWithCGImage:bitmapContext];
    // image is retained by the property setting above, so we can 
    // release the original
    CGImageRelease(bitmapContext);

    // return the image
    return theImage;
}  

이제 코드 몇 줄만 있으면 됩니다.는 내 컨트롤러 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅜㅜㅜㅜㅜㅜㅜviewDidLoad할 수 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜUIViewlayoutSubviews예제의 방법



- (void)viewDidLoad {

    // Create the mask image you need calling the previous function
    UIImage *mask = MTDContextCreateRoundedMask( self.view.bounds, 50.0, 50.0, 0.0, 0.0 );
    // Create a new layer that will work as a mask
    CALayer *layerMask = [CALayer layer];
    layerMask.frame = self.view.bounds;       
    // Put the mask image as content of the layer
    layerMask.contents = (id)mask.CGImage;       
    // set the mask layer as mask of the view layer
    self.view.layer.mask = layerMask;              

    // Add a backaground color just to check if it works
    self.view.backgroundColor = [UIColor redColor];
    // Add a test view to verify the correct mask clipping
    UIView *testView = [[UIView alloc] initWithFrame:CGRectMake( 0.0, 0.0, 50.0, 50.0 )];
    testView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:testView];
    [testView release];

    [super viewDidLoad];
}

솔루션 2

이 솔루션은 좀 더 "더러운" 솔루션입니다.기본적으로 필요한 둥근 모서리(모든 모서리)로 마스크 도면층을 작성할 수 있습니다.그런 다음 마스크 레이어의 높이를 모서리 반지름 값만큼 높여야 합니다.이렇게 하면 아래쪽 둥근 모서리가 숨겨지고 위쪽 둥근 모서리만 볼 수 있습니다.나는 코드를 바로 그 안에 넣었습니다.viewDidLoad할 수 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜUIViewlayoutSubviews예제의 방법

  

- (void)viewDidLoad {

    // set the radius
    CGFloat radius = 50.0;
    // set the mask frame, and increase the height by the 
    // corner radius to hide bottom corners
    CGRect maskFrame = self.view.bounds;
    maskFrame.size.height += radius;
    // create the mask layer
    CALayer *maskLayer = [CALayer layer];
    maskLayer.cornerRadius = radius;
    maskLayer.backgroundColor = [UIColor blackColor].CGColor;
    maskLayer.frame = maskFrame;

    // set the mask
    self.view.layer.mask = maskLayer;

    // Add a backaground color just to check if it works
    self.view.backgroundColor = [UIColor redColor];
    // Add a test view to verify the correct mask clipping
    UIView *testView = [[UIView alloc] initWithFrame:CGRectMake( 0.0, 0.0, 50.0, 50.0 )];
    testView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:testView];
    [testView release];

    [super viewDidLoad];
}

이게 도움이 되길 바랍니다.차오!

몇 가지 답변과 의견을 살펴보니, 저는 다음과 같은 것을 발견했습니다.UIBezierPath bezierPathWithRoundedRect그리고.CAShapeLayer가장 간단하고 직선적인 방법매우 복잡한 경우에는 적합하지 않을 수 있지만, 가끔 코너를 도는 경우에는 빠르고 부드럽게 작동합니다.

마스크에 적절한 코너를 설정하는 단순화된 도우미를 만들었습니다.

-(void) setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
    UIBezierPath* rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(10.0, 10.0)];

    CAShapeLayer* shape = [[CAShapeLayer alloc] init];
    [shape setPath:rounded.CGPath];

    view.layer.mask = shape;
}

이 기능을 사용하려면 다음과 같은 적절한 UIRectCorner 열거형으로 전화하기만 하면 됩니다.

[self setMaskTo:self.photoView byRoundingCorners:UIRectCornerTopLeft|UIRectCornerBottomLeft];

그룹화된 UITableViewCell에서 사진의 모서리를 둥글게 만드는 데 사용하므로 값을 적절히 변경해야 하는 경우 10.0 반경이 적합합니다.

편집: 이전에 답변한 내용이 이 링크와 매우 유사하다는 것을 참조하십시오.필요한 경우에도 이 답변을 추가 편의 기능으로 사용할 수 있습니다.


편집: Same code as UIView extension in Swift 3

extension UIView {
    func maskByRoundingCorners(_ masks:UIRectCorner, withRadii radii:CGSize = CGSize(width: 10, height: 10)) {
        let rounded = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: masks, cornerRadii: radii)

        let shape = CAShapeLayer()
        shape.path = rounded.cgPath

        self.layer.mask = shape
    }
}

하기 위해서는 전화를 걸면 .maskByRoundingCorner아무렇게나UIView:

view.maskByRoundingCorners([.topLeft, .bottomLeft])

저는 @lomanf의 대답에 이 모든 것을 맞출 수 없었습니다.그래서 저는 그것을 답으로 추가합니다.

@lomanf가 말했듯이, 하위 도면층이 경로의 경계를 벗어나는 것을 방지하기 위해 도면층 마스크를 추가해야 합니다.하지만 지금은 훨씬 쉬워졌습니다.iOS 3.2 이상을 대상으로 하는 한 쿼츠로 이미지를 만들어 마스크로 설정할 필요가 없습니다. 히단생수있다니습할을 수 .CAShapeLayerUIBezierPath그리고 그것을 마스크로 사용합니다.

또한 도면층 마스크를 사용하는 경우 마스크를 추가할 때 마스킹할 도면층이 도면층 계층의 일부가 아닌지 확인합니다.그렇지 않으면 동작이 정의되지 않습니다.보기가 이미 계층 구조에 있는 경우에는 해당 보기를 해당 보기에서 제거하고 마스킹한 다음 원래 위치로 되돌려야 합니다.

CAShapeLayer *maskLayer = [CAShapeLayer layer];
UIBezierPath *roundedPath = 
  [UIBezierPath bezierPathWithRoundedRect:maskLayer.bounds
                        byRoundingCorners:UIRectCornerTopLeft |
                                          UIRectCornerBottomRight
                              cornerRadii:CGSizeMake(16.f, 16.f)];    
maskLayer.fillColor = [[UIColor whiteColor] CGColor];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [roundedPath CGPath];

//Don't add masks to layers already in the hierarchy!
UIView *superview = [self.view superview];
[self.view removeFromSuperview];
self.view.layer.mask = maskLayer;
[superview addSubview:self.view];

코어 애니메이션 렌더링 작동 방식으로 인해 마스킹 작업이 상대적으로 느립니다.각 마스크에는 추가 렌더링 패스가 필요합니다.따라서 마스크를 적게 사용해야 합니다.

의 가장 점중중 식 의 가 사 없 입 는 다 생 정 할 성 의 를 자 용 이 상 더 는 나 하 분 장 부 좋 은 ▁one 다 ▁is 니 ▁a ▁that ▁approach ▁you 이 입UIView 및오라드이버드를 재정의합니다.drawRect:이렇게 하면 코드가 더 단순해지고 더 빨라질 수 있습니다.

Nathan의 네쓴의예들다같음은다만범니들습었에 .UIView무미건조한 원칙을 고수할 수 있도록 하는 것.추가 작업 없이:

UIView+원순화.h

#import <UIKit/UIKit.h>

@interface UIView (Roundify)

-(void)addRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii;
-(CALayer*)maskForRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii;

@end

UIView+원형화.m

#import "UIView+Roundify.h"

@implementation UIView (Roundify)

-(void)addRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
    CALayer *tMaskLayer = [self maskForRoundedCorners:corners withRadii:radii];
    self.layer.mask = tMaskLayer;
}

-(CALayer*)maskForRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = self.bounds;

    UIBezierPath *roundedPath = [UIBezierPath bezierPathWithRoundedRect:
        maskLayer.bounds byRoundingCorners:corners cornerRadii:radii];
    maskLayer.fillColor = [[UIColor whiteColor] CGColor];
    maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
    maskLayer.path = [roundedPath CGPath];

    return maskLayer;
}

@end

연락처:

[myView addRoundedCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight
                withRadii:CGSizeMake(20.0f, 20.0f)];

P에서 조금 더 확장하기 위해.변 답 저 다 음 과 같 은 특 객 반 체 않 썼 니 습 다 다 시 방 을 법 때 문 의 에 았 기 는 올 지 림 하 를 정 니 썼 ▁such ▁like ▁so 다 ▁i ▁method ▁l 습 ▁the ▁certain ▁objects ▁answer ▁it ▁as ▁l 의 ▁as ' s▁rounding ▁re 답 ' 다 변 ▁wasn 저 시UIButton

- (void)setMaskTo:(id)sender byRoundingCorners:(UIRectCorner)corners withCornerRadii:(CGSize)radii
{
    // UIButton requires this
    [sender layer].cornerRadius = 0.0;

    UIBezierPath *shapePath = [UIBezierPath bezierPathWithRoundedRect:[sender bounds]
                                                byRoundingCorners:corners
                                                cornerRadii:radii];

    CAShapeLayer *newCornerLayer = [CAShapeLayer layer];
    newCornerLayer.frame = [sender bounds];
    newCornerLayer.path = shapePath.CGPath;
    [sender layer].mask = newCornerLayer;
}

그리고 그것을 다음으로 부릅니다.

[self setMaskTo:self.continueButton byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight withCornerRadii:CGSizeMake(3.0, 3.0)];

만약 당신이 스위프트에서 그것을 하고 싶다면 당신은 의 확장을 사용할 수 있습니다.UIView이렇게 하면 모든 하위 클래스에서 다음 방법을 사용할 수 있습니다.

import QuartzCore

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

사용 예:

self.anImageView.roundCorner(.topRight, radius: 10)

승인된 답변을 확장하여 하위 호환성을 추가합니다.iOS 11 이전 버전에서는 view.layer.masked Corners를 사용할 수 없습니다.그래서 이렇게 할 수 있습니다.

if #available(iOS 11.0, *) {
    myView.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]
} else {
    myView.maskByRoundingCorners([.topLeft, .topRight])
}

extension UIView{
    func maskByRoundingCorners(_ masks:UIRectCorner, withRadii radii:CGSize = CGSize(width: 10, height: 10)) {
        let rounded = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: masks, cornerRadii: radii)

        let shape = CAShapeLayer()
        shape.path = rounded.cgPath

        self.layer.mask = shape
    }
}

코드 재사용을 개선하기 위해 UIView 확장으로 maskByRoundingCorners를 작성했습니다.

@SachinVsSachin 및 @P에 대한 크레딧.L :) 저는 그것을 더 좋게 만들기 위해 그들의 코드를 결합했습니다.

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(5, 5, self.bounds.size.width-10, self.bounds.size.height-10)
                                               byRoundingCorners:UIRectCornerAllCorners
                                                     cornerRadii:CGSizeMake(12.0, 12.0)];

필요에 따라 "모든 코너"를 변경합니다.

제공된 모든 솔루션이 목표를 달성합니다.그렇지만,UIConstraints때때로 이것을 날려버릴 수 있습니다.

예를 들어, 아래쪽 모서리는 반올림해야 합니다.을 UIView로 .viewDidLayoutSubviews방법.

강조 표시:

UIBezierPath *maskPath = [UIBezierPath
    bezierPathWithRoundedRect:roundedView.bounds byRoundingCorners:
    (UIRectCornerTopRight | UIRectCornerBottomRight) cornerRadii:CGSizeMake(16.0, 16.0)];

위의 코드 스니펫은 이 코드가 설정된 경우에만 오른쪽 상단 모서리를 반올림합니다.viewDidLoad .ㅠㅠroundedView.bounds 조건이 된 후 변경됩니다.UIView.

마스크를 만들어 뷰의 도면층에 설정

코드부터 시작하여 아래의 스니펫과 같은 것을 사용할 수 있습니다.

이것이 당신이 추구하는 결과인지 잘 모르겠습니다.또한 시스템이 Rect를 호출할 때 Rect의 일부만 다시 그려달라고 요청하는 경우에도 매우 이상하게 작동합니다.위에서 언급한 네반의 접근 방식이 더 나은 방법일 수 있습니다.

 // make sure the view's background is set to [UIColor clearColor]
- (void)drawRect:(CGRect)rect
{
    CGFloat radius = 10.0;
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, rect.size.width/2, rect.size.height/2);
    CGContextRotateCTM(context, M_PI); // rotate so image appears right way up
    CGContextTranslateCTM(context, -rect.size.width/2, -rect.size.height/2);

    CGContextBeginPath(context);

    CGContextMoveToPoint(context, rect.origin.x, rect.origin.y);
    CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI / 2, 1);
    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);
    CGContextClip(context); 

    // now do your drawing, e.g. draw an image
    CGImageRef anImage = [[UIImage imageNamed:@"image.jpg"] CGImage];
    CGContextDrawImage(context, rect, anImage);    
}

약간 구식이지만 비교적 간단한 방법(하위 분류, 마스킹 등 없음)은 두 개의 UIV 뷰를 갖는 것입니다. 다 둘다포와 함께 사용clipToBounds = YES자식 보기에서 둥근 모서리를 설정한 다음 부모 보기 내에 배치하여 원하는 모서리가 직선으로 잘리도록 합니다.

UIView* parent = [[UIView alloc] initWithFrame:CGRectMake(10,10,100,100)];
parent.clipsToBounds = YES;

UIView* child = [[UIView alloc] new];
child.clipsToBounds = YES;
child.layer.cornerRadius = 3.0f;
child.backgroundColor = [UIColor redColor];
child.frame = CGRectOffset(parent.bounds, +4, -4);


[parent addSubView:child];

대각선으로 반대쪽 모서리 두 개를 둥글게 처리하는 경우를 지원하지 않습니다.

베지어 경로가 정답입니다. 추가 코드가 필요하면 이 코드가 저를 위해 작동했습니다. https://stackoverflow.com/a/13163693/936957

UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:_backgroundImageView.bounds 
                                 byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) 
                                       cornerRadii:CGSizeMake(3.0, 3.0)];

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

UIBizerPath 솔루션입니다.

- (void) drawRect:(CGRect)rect {

    [super drawRect:rect];

    //Create shape which we will draw. 
    CGRect rectangle = CGRectMake(2, 
                                  2, 
                                  rect.size.width - 4,
                                  rect.size.height - 4);

    //Create BezierPath with round corners
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rectangle 
                                                   byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight 
                                                         cornerRadii:CGSizeMake(10.0, 10.0)];

    //Set path width
    [maskPath setLineWidth:2];

    //Set color
    [[UIColor redColor] setStroke];

    //Draw BezierPath to see it
    [maskPath stroke];
}

이것은 다음과 같은 일부 사항이 올바르게 설정된 경우에만 작동할 수 있습니다.

  1. clipsToBounds를 YES로 설정해야 합니다.
  2. 불투명은 NO여야 합니다.
  3. backgroundColor는 "clearColor"여야 합니다(이것에 대해 완전히 확신할 수 없습니다).
  4. contentMode는 drawRect가 아닌 경우 자주 호출되지 않으므로 "UIContentModeRedraw"여야 합니다.
  5. [superdrawRect:rect]는 CGC ContextClip 다음에 호출해야 합니다.
  6. 보기에 임의 하위 보기가 포함되지 않을 수 있습니다(이것에 대해서는 확실하지 않음).
  7. 드로우라이트를 트리거하려면 "니즈디스플레이:"를 한 번 이상 설정해야 합니다.

UITableView의 상위 두 모서리를 반올림하려는 것은 알고 있지만, 어떤 이유에서인지 가장 좋은 해결책은 다음과 같습니다.

self.tableView.layer.cornerRadius = 10;

프로그래밍적으로 네 모서리를 모두 둥글게 만들어야 하지만, 어떤 이유에서인지 상위 두 모서리만 둥글게 만듭니다.**아래 스크린샷을 참조하여 위에 작성한 코드의 효과를 확인하십시오.

이것이 도움이 되길 바랍니다!

enter image description here

당신은 아마 한계에 도달해야 할 것입니다.선 추가

self.clipsToBounds = YES

그 속성을 설정하기 위한 코드의 어딘가.

언급URL : https://stackoverflow.com/questions/4847163/round-two-corners-in-uiview