Làm tròn hai góc trong UIView


82

Cách đây ít lâu, tôi đã đăng một câu hỏi về việc chỉ làm tròn hai góc của một chế độ xem và đã nhận được phản hồi tuyệt vời, nhưng đang gặp sự cố khi triển khai. Đây là phương thức drawRect:

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

Phương thức đang được gọi, nhưng dường như không ảnh hưởng đến kết quả của chế độ xem. Bất kỳ ý tưởng tại sao?

Câu trả lời:


103

CACornerMask được giới thiệu trong iOS 11, giúp xác định topleft, topright, bottomleft, dưới cùng bên phải trong lớp xem. Dưới đây là ví dụ để sử dụng.

Ở đây tôi chỉ cố gắng làm tròn hai góc trên cùng:

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

Tham khảo FYI:


Điều này có vẻ giống như cách thành ngữ để làm điều này bây giờ. Nắm bắt tốt!
Jumhyn

2
Lưu ý rằng chiều rộng / chiều cao của chế độ xem vẫn phải bằng ít nhất hai lần bán kính góc, nếu không sẽ xuất hiện các hiện vật lạ. ví dụ: chỉ làm tròn các góc dưới cùng, bán kính 20 và chiều cao chỉ 30 dẫn đến ô hẹp hơn rõ ràng (bằng một hoặc hai pixel).
Graham Perks

Lưu ý rằng đây là cách đúng nếu chế độ xem của bạn thay đổi khung của nó (có thể do UITextView đang mở rộng, như trong trường hợp của tôi - stackoverflow.com/questions/50926215/… ). Nếu bạn đặt UIBezierPath, nó sẽ không tự động cập nhật khi khung thay đổi.
thomers

Lưu ý rằng nó chỉ có sẵn từ iOS 11 trở lênif #available(iOS 11.0, *) { }
Trí Ngô Minh

2
Câu trả lời chính xác. Hóa ra cài đặt clipsToBoundsthành truekhông cần thiết. Bạn có thể che các góc và thêm bóng vào lớp khi clipsToBoundsfalse. Có ích cho tôi.
Au Ris

72


Theo như tôi biết, nếu bạn cũng cần che các chế độ xem phụ, bạn có thể sử dụng tính năng tạo CALayermặt nạ. Có 2 cách để làm điều này. Cái đầu tiên thanh lịch hơn một chút, cái thứ hai là một giải pháp thay thế :-) nhưng nó cũng nhanh. Cả hai đều dựa trên CALayermặt nạ. Tôi đã sử dụng cả hai phương pháp trong một vài dự án vào năm ngoái và tôi hy vọng bạn có thể tìm thấy điều gì đó hữu ích.

Giải pháp 1

Trước hết, tôi tạo hàm này để tạo mặt nạ hình ảnh trên fly ( UIImage) với góc tròn mà tôi cần. Chức năng này về cơ bản cần 5 tham số: giới hạn của hình ảnh và 4 bán kính góc (trên cùng bên trái, trên cùng bên phải, dưới cùng bên trái và dưới cùng bên phải).


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;
}  

Bây giờ bạn chỉ cần vài dòng mã. Tôi đặt nội dung trong viewDidLoadphương thức viewController của mình vì nó nhanh hơn nhưng bạn cũng có thể sử dụng nó theo tùy chọn của mình UIViewvới layoutSubviewsphương thức trong ví dụ.



- (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];
}

Giải pháp 2

Dung dịch này "bẩn" hơn một chút. Về cơ bản, bạn có thể tạo một lớp mặt nạ với góc tròn mà bạn cần (tất cả các góc). Sau đó, bạn nên tăng chiều cao của lớp mặt nạ bằng giá trị của bán kính góc. Bằng cách này, các góc tròn dưới cùng bị ẩn và bạn chỉ có thể nhìn thấy góc tròn trên. Tôi chỉ đặt mã trong viewDidLoadphương thức vì nó nhanh hơn nhưng bạn cũng có thể sử dụng nó theo tùy chỉnh của mình UIViewvới layoutSubviewsphương thức trong ví dụ.

  

- (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];
}

Hi vọng điêu nay co ich. Chào!


Cuối cùng cũng thành công! Cám ơn rất nhiều! Lợi thế của cái này so với cái kia là gì?
Jumhyn

Dù sao thì ngay cả giải pháp CAShapeLayer cũng hoàn hảo. Bạn có thể vẽ các CGPath sử dụng UIBezierPath (mà nhu cầu 3.2+) hoặc chỉ với các phương pháp CGPath (CGPathMoveToPoint, CGPathAddQuadCurveToPoint vv)
lomanf

1
Làm cách nào để mặt nạ tự phục hồi sau khi xoay từ dọc sang ngang?
chourobin

Đừng quên để thêm layer.masksToBounds = YES
Dmitry Salnikov

Làm thế nào để làm cho nó chỉ làm tròn góc trên và dưới cùng bên phải?
PHÁT

69

Lướt qua một vài câu trả lời và nhận xét, tôi phát hiện ra rằng cách sử dụng UIBezierPath bezierPathWithRoundedRectCAShapeLayercách đơn giản nhất và dễ hiểu nhất. Nó có thể không thích hợp cho những trường hợp quá phức tạp, nhưng đối với việc làm tròn các góc không thường xuyên, nó hoạt động nhanh và trơn tru đối với tôi.

Tôi đã tạo một trình trợ giúp đơn giản hóa để đặt góc thích hợp trong mặt nạ:

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

Để sử dụng nó, chỉ cần gọi với enum UIRectCorner thích hợp, ví dụ:

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

Xin lưu ý rằng đối với tôi, tôi sử dụng nó để làm tròn các góc ảnh trong UITableViewCell được nhóm lại, bán kính 10.0 phù hợp với tôi, nếu cần chỉ cần thay đổi giá trị cho phù hợp.

CHỈNH SỬA: chỉ cần nhận thấy một câu trả lời trước đây rất giống với câu trả lời này ( liên kết ). Bạn vẫn có thể sử dụng câu trả lời này như một chức năng tiện lợi bổ sung nếu cần.


CHỈNH SỬA: Mã giống như tiện ích mở rộng UIView trong 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
    }
}

Để sử dụng nó, đơn giản hãy gọi maskByRoundingCornervào bất kỳ UIView:

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

Điều này có khả thi trong khi vẫn thêm đường viền không?
Unome

2
Tôi đã phải đưa chức năng cuộc gọi vào DispatchQueue.main.async {} bởi vì chỉ có một góc đã làm tròn ...
Maksim Kniazev

1
@MaksimKniazev vâng, điều này phải được gọi từ luồng chính (UI). Quy tắc này áp dụng cho bất kỳ hành động nào chạm vào giao diện người dùng, chúng phải được gọi từ chuỗi chính hoặc một số điều kỳ lạ có thể xảy ra.
PL

1
chỉ có một góc dường như tròn. đã thử @MaksimKniazev giải pháp cũng không hoạt động.
Gustavo_fringe

Tôi gặp sự cố tương tự như @Gustavo_fringe, trong đó góc trên cùng bên phải không được cắt / che để giới hạn bên trong (đã thay đổi kích thước) một chế độ xem xib, tôi không biết cách khắc phục nó.
CyberMew

57

Tôi không thể đưa tất cả điều này vào một bình luận cho câu trả lời của @ lomanf. Vì vậy, tôi thêm nó như một câu trả lời.

Giống như @lomanf đã nói, bạn cần thêm mặt nạ lớp để ngăn các lớp con vẽ ra bên ngoài giới hạn đường dẫn của bạn. Tuy nhiên, bây giờ nó dễ dàng hơn rất nhiều. Miễn là bạn đang nhắm mục tiêu iOS 3.2 trở lên, bạn không cần tạo hình ảnh bằng thạch anh và đặt nó làm mặt nạ. Bạn chỉ cần tạo một CAShapeLayervới a UIBezierPathvà sử dụng nó làm mặt nạ.

Ngoài ra, khi sử dụng mặt nạ lớp, hãy đảm bảo rằng lớp bạn đang tạo mặt nạ không nằm trong bất kỳ phân cấp lớp nào khi bạn thêm mặt nạ. Nếu không thì hành vi là không xác định. Nếu chế độ xem của bạn đã có trong hệ thống phân cấp, bạn cần xóa nó khỏi chế độ xem siêu cấp của nó, che nó đi, sau đó đặt nó trở lại vị trí cũ.

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];

Do cách kết xuất Core Animation hoạt động, tạo mặt nạ là một hoạt động tương đối chậm. Mỗi mặt nạ yêu cầu một đường kết xuất bổ sung. Vì vậy, hãy sử dụng mặt nạ một cách tiết kiệm.

Một trong những phần tốt nhất của phương pháp này là bạn không cần tạo tùy chỉnh UIViewvà ghi đè drawRect:nữa. Điều này sẽ làm cho mã của bạn đơn giản hơn và thậm chí có thể nhanh hơn.


Có vẻ hứa hẹn, nhưng chế độ xem không xuất hiện. Bất kỳ ý tưởng tại sao?
Jumhyn

10
Bạn đang sử dụng maskLayer.bounds để định kích thước UIBezierPath nhưng giá trị của giới hạn là CGRectZero. Bạn cần thêm dòng mã thứ hai (ngay sau khi khởi tạo CAShapeLayer) như maskLayer.frame = self.view.bounds;
lomanf

1
Vâng bạn đã đúng. Tôi đã trích xuất mã đó từ một ví dụ khác, nơi lớp hình dạng đã tồn tại. Xin lỗi vì điều đó.
Nathan Eror

Nó hoạt động hoàn hảo! Thêm maskLayer.frame = self.view.bounds, như đã nêu ở trên, make là công việc. Nếu không, các lượt xem sẽ không hiển thị.
Daniel Ribeiro

+1 Điều này hoạt động khá tốt. Trong thực tế, tôi nghĩ rằng tôi thích phương pháp này hơn giải pháp số 2 của lomanf. Phương pháp này dễ dàng cho phép bạn thêm vòng cung vào các góc đối diện (ví dụ: Trên cùng bên phải + Dưới cùng bên trái), và nó chắc chắn ngắn hơn giải pháp đầu tiên của lomanf. Tuyệt vời Nathan!
FreeAsInBeer

30

Tôi đã lấy ví dụ của Nathan và tạo một danh mục UIViewđể cho phép một danh mục tuân thủ các nguyên tắc KHÔ. Nếu không có thêm lời khuyên:

UIView + Roundify.h

#import <UIKit/UIKit.h>

@interface UIView (Roundify)

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

@end

UIView + Roundify.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

Để gọi:

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

2
Đừng quên nhập <QuartzCore / QuatzCore.h> trong tệp .m và cũng thêm QuartzCore.framework vào dự án
DEzra

+1 Điều tuyệt vời, tuy nhiên, một câu hỏi, mục đích của việc xóa / đọc chế độ xem thành chế độ xem siêu cao của nó là gì?
eladleb

@eladleb: Mã cơ bản ở đây là của Nathan, tôi chỉ đơn giản là tối ưu hóa nó một chút. Từ nghiên cứu mà tôi đã thực hiện, tôi không thể tìm thấy lý do hợp lệ để xóa chế độ xem trước khi áp dụng mặt nạ lớp. Do đó, tôi tin rằng sẽ an toàn nếu bỏ qua mã đó.
FreeAsInBeer

@FreeAsInBeer - Tôi đồng ý, sau đó tôi sẽ bỏ phiếu cho việc xóa nó, điều này thực sự thay đổi thứ tự z của chế độ xem và có thể tạo ra nhiều vấn đề hơn ..
eladleb

Đã giải quyết vấn đề tôi gặp phải bằng cách thêm các góc tròn vào TableView: Tôi đã thêm tiêu đề, các góc trên cùng được làm tròn và thêm chân trang với các góc dưới cùng được làm tròn. Cảm ơn.
ban đêm

24

Để mở rộng một chút về câu trả lời của PL, tôi đã viết lại phương thức như vậy vì nó không làm tròn các đối tượng nhất định một UIButtoncách chính xác

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

Và gọi nó bằng

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

12

Nếu bạn muốn làm điều đó trong Swift, bạn có thể sử dụng phần mở rộng của a UIView. Làm như vậy, tất cả các lớp con sẽ có thể sử dụng phương thức sau:

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

Ví dụ sử dụng:

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

3

Mở rộng câu trả lời được chấp nhận, hãy để chúng tôi thêm khả năng tương thích ngược cho nó. Trước iOS 11, view.layer.maskedCorners không khả dụng. Vì vậy, chúng tôi có thể làm như thế này

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

Chúng tôi đã viết maskByRoundsCorners dưới dạng một phần mở rộng UIView để nó cải thiện việc tái sử dụng mã.

Tín dụng cho @SachinVsSachin và @PL :) Tôi đã kết hợp mã của họ để làm cho nó tốt hơn.


2
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)];

thay đổi "AllCorners" theo nhu cầu của bạn.


vậy bạn phải thực hiện việc này trong UIView hay có thể thực hiện việc này trong UIViewController? Bạn sẽ làm gì với UIBezierPath khi bạn đã tạo xong nó? Cảm ơn.
Stonebreaker

2

Tất cả các giải pháp được cung cấp đều đạt được mục tiêu. Nhưng, UIConstraintsđôi khi có thể làm nổ tung điều này.

Ví dụ, các góc dưới cùng cần được làm tròn. Nếu giới hạn khoảng cách giữa chiều cao hoặc đáy được đặt thành UIView cần được làm tròn, thì các đoạn mã làm tròn các góc cần được chuyển sang viewDidLayoutSubviewsphương thức.

Đánh dấu:

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

Đoạn mã trên sẽ chỉ làm tròn góc trên cùng bên phải nếu mã này được đặt trong viewDidLoad. Bởi vì roundedView.boundssẽ thay đổi sau khi các ràng buộc cập nhật UIView.



1

Bắt đầu với mã của bạn, bạn có thể đi với một cái gì đó giống như đoạn mã bên dưới.

Tôi không chắc liệu đây có phải là kết quả bạn đang theo đuổi hay không. Cũng cần lưu ý rằng nếu / khi hệ thống gọi drawRect: một lần nữa, chỉ yêu cầu vẽ lại một phần của trực tràng, điều này sẽ hoạt động rất kỳ lạ. Cách tiếp cận của Nevan , đã nêu ở trên, có thể là một cách tốt hơn để đi.

 // 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);    
}

Tôi đã tạo ra một chế độ xem với các góc tròn, nhưng tôi không thể làm cho nó cắt bất kỳ chế độ xem phụ nào của nó. Nếu tôi đặt một chế độ xem có các góc vuông ở cạnh của chế độ xem của tôi, nó vẫn che các góc tròn của chế độ xem siêu cấp của nó. Bất kỳ ý tưởng tại sao điều này đang xảy ra?
Jumhyn

Bởi vì việc cắt bớt -drawRect:chỉ ảnh hưởng đến chế độ xem bạn đang vẽ. Các lượt xem phụ của nó vẽ độc lập sau khi bản vẽ của bạn hoàn thành.
Jonathan Grynspan

1

Một cách hơi hacky, nhưng tương đối đơn giản (không phân lớp, tạo mặt nạ, v.v.) để làm điều này là có hai UIView. Cả hai với clipToBounds = YES. Đặt các góc tròn trên chế độ xem con, sau đó định vị nó trong chế độ xem chính để các góc bạn muốn thẳng sẽ bị cắt.

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];

Không hỗ trợ trường hợp bạn muốn làm tròn hai góc chéo đối diện.


1

Đường dẫn Bezier là anwer, nếu bạn cần mã bổ sung, đường dẫn này đã làm việc cho tôi: 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];

1

Giải pháp UIBezierPath.

- (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];
}

0

Điều này chỉ có thể hoạt động nếu một số điều được đặt chính xác:

  1. clipsToBounds phải được đặt thành CÓ
  2. mờ đục phải KHÔNG
  3. backgroundColor phải là "clearColor" (tôi không hoàn toàn chắc chắn về điều này)
  4. contentMode phải là "UIContentModeRedraw" vì drawRect không được gọi thường xuyên nếu không
  5. [super drawRect: direct] phải được gọi sau CGContextClip
  6. Chế độ xem của bạn không được chứa các lượt xem phụ tùy ý (không chắc chắn về điều này)
  7. Đảm bảo đặt "needDisplay:" ít nhất một lần để kích hoạt bản vẽ của bạn

0

Tôi nhận ra rằng bạn đang cố gắng làm tròn hai góc trên cùng của UITableView, nhưng vì một số lý do, tôi thấy rằng giải pháp tốt nhất là sử dụng:

self.tableView.layer.cornerRadius = 10;

Theo chương trình, nó sẽ làm tròn tất cả bốn góc, nhưng vì lý do nào đó, nó chỉ làm tròn hai góc trên cùng. ** Vui lòng xem ảnh chụp màn hình bên dưới để biết tác dụng của mã tôi đã viết ở trên.

Tôi hi vọng cái này giúp được!

nhập mô tả hình ảnh ở đây


1
Nếu nó không làm tròn tất cả các góc của UITableViewnó thì phải có mã ở nơi khác làm cho nó UITableViewkhông có các góc tròn ở phía dưới, hoặc thậm chí có thể là một UIViewhoặc UILabelcái đó đang che phần dưới cùng của UITableView.
Josh Valdivieso

-1

Bạn có thể phải clip để giới hạn. Thêm dòng

self.clipsToBounds = YES

ở đâu đó trong mã để đặt thuộc tính đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.