Округленный UIView с использованием CALayers-только некоторые углы-как?

в моем приложении - есть четыре кнопки названы следующим образом:

  • верхний левый
  • нижний - левый
  • верхний правый
  • нижний правый

над кнопками есть вид изображения (или UIView).

теперь предположим, что пользователь нажимает на верхнюю левую кнопку. Выше изображение / вид должны быть закруглены в этом конкретном углу.

У меня возникли некоторые трудности в применении скругленных углов к Наследник 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]];

вышеуказанный код применяет округлость к каждому из углов предоставленного вида. Вместо этого я просто хотел применить округлость к выбранным углам, таким как - top / top+left / bottom+right и т. д.

это возможно? Как?

13 ответов


я использовал ответ на как создать круглый угловой UILabel на iPhone? и код как округленный вид прямой кишки с прозрачностью на iphone? чтобы сделать этот код.

затем я понял, что ответил на неправильный вопрос (дал округленный UILabel вместо UIImage), поэтому я использовал этот код, чтобы изменить его:

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

сделать 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 вызовы и удаление некоторых значений radius в коде. Код для всех углов есть, поэтому используйте это как начальная точка и удалить части, которые создают углы, которые вам не нужны. Обратите внимание, что вы также можете сделать прямоугольники с 2 или 3 закругленными углами, если хотите.

код не идеален, но я уверен, что вы можете убирать его немного.

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 там для этого, чтобы работать.


начиная с 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;

и это все-не возиться вручную, определяя фигуры в основной графике, не создавать маскирующие изображения в Photoshop. Слой даже не нуждается в аннулировании. Применение закругленного угла или изменение на новый углу так же просто, как определение нового UIBezierPath и с помощью CGPath как путь слоя маски. The


я использовал этот код в мой код и он работает правильно на 100%. Вы можете изменить любой Кордер, изменив одно свойство "byRoundingCorners: UIRectCornerBottomLeft"

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

пример Stuarts для округления определенных углов отлично работает. Если вы хотите округлить несколько углов, таких как верхний левый и правый, вот как это сделать

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

Спасибо за обмен. Здесь я хотел бы поделиться решением на 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

есть более простой и быстрее ответ, который может работать в зависимости от ваших потребностей, а также работает с тенями. вы можете установить maskToBounds на superlayer в true и смещать дочерние слои так, чтобы 2 их угла находились за пределами границ superlayer, эффективно вырезая закругленные углы с 2 сторон.

конечно, это работает только тогда, когда вы хотите иметь только 2 закругленных угла на одной стороне, и содержимое слоя выглядит одинаково, когда вы отсекаете несколько пикселей от одна сторона. работает отлично подходит для того, чтобы гистограммы округлены только на верхней стороне.


расширение CALayer с Swift 3 и выше синтаксис

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

в iOS 11 теперь мы можем обойти только некоторые углы

let view = UIView()

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

посмотреть этот вопрос. Вам придется нарисовать свой собственный прямоугольник до CGPath с некоторые закругленные углы, добавить CGPath на CGContext а затем закрепите его с помощью CGContextClip.

вы также можете нарисовать округлую прямую линию с Альфа-значениями для изображения, а затем использовать это изображение для создания нового слоя, который вы задаете как mask собственность (см. документацию Apple).


на полдесятилетия позже, но я думаю, что в настоящее время люди делают это не на 100% правильно. У многих людей была проблема, что использование метода UIBezierPath + CAShapeLayer мешает автоматической компоновке, особенно когда он установлен на раскадровке. На это нет ответов, поэтому я решил добавить свой собственный.

существует очень простой способ обойти это: нарисовать закругленные углы в


округление только некоторых углов не будет хорошо играть с автоматическим изменением размера или автоматической компоновкой.

таким образом, другой вариант - использовать regular cornerRadius и скрыть углы, которые вы не хотите под другим видом или за пределами его границ просмотра, убедившись, что он настроен на клип его содержимое.


добавить к ответ и дополнение Я создал простой, многоразовые UIView в Swift. В зависимости от вашего варианта использования вы можете внести изменения (избегайте создания объектов на каждом макете и т. д.), но я хотел, чтобы все было как можно проще. Расширение позволяет применить это к другим представлениям (ex. 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];