Некоторое время назад я разместил вопрос об округлении только двух углов обзора и получил отличный ответ, но у меня возникли проблемы с его реализацией. Вот мой метод 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);
}
Метод вызывается, но, похоже, не влияет на результат просмотра. Есть идеи, почему?
ios
objective-c
iphone
rounded-corners
Джамхин
источник
источник
if #available(iOS 11.0, *) { }
clipsToBounds
наtrue
нет необходимости. Вы можете замаскировать углы и добавить тень к слою, когдаclipsToBounds
естьfalse
. Пригодился мне.Насколько я знаю, если вам также нужно замаскировать подвиды, вы можете использовать
CALayer
маскировку. Это можно сделать двумя способами. Первый вариант немного элегантнее, второй - обходной путь :-), но он также быстрый. Оба основаны наCALayer
маскировке. В прошлом году я использовал оба метода в паре проектов, и надеюсь, вы найдете что-нибудь полезное.Решение 1
Прежде всего, я создал эту функцию для генерации маски изображения на лету (
UIImage
) с нужным мне скругленным углом. Эта функция по существу требует 5 параметров: границы изображения и 4 радиуса угла (верхний левый, верхний правый, нижний левый и нижний правый).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; }
Теперь вам просто нужно несколько строк кода. Я положил вещи в моем ViewController
viewDidLoad
метод , потому что это быстрее , но вы можете использовать его также в пользовательскихUIView
сlayoutSubviews
методом примера.- (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
методе , потому что это быстрее , но вы можете использовать его также в пользовательскомUIView
сlayoutSubviews
методом примера.- (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 отлично подходит для меня, если нужно просто изменить значение соответствующим образом.
РЕДАКТИРОВАТЬ: просто обратите внимание на предыдущий ответ, очень похожий на этот ( ссылка ). Вы все еще можете использовать этот ответ как дополнительную удобную функцию, если это необходимо.
EDIT: тот же код, что и расширение UIView в 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
:источник
Я не мог вместить все это в комментарий к ответу @lomanf. Я добавляю это как ответ.
Как сказал @lomanf, вам нужно добавить маску слоя, чтобы подслои не отображались за пределами вашего пути. Однако сейчас это сделать намного проще. Пока вы ориентируетесь на iOS 3.2 или выше, вам не нужно создавать изображение с кварцем и устанавливать его в качестве маски. Вы можете просто создать с
CAShapeLayer
помощью aUIBezierPath
и использовать его как маску.Кроме того, при использовании масок слоев убедитесь, что слой, который вы маскируете, не является частью какой-либо иерархии слоев при добавлении маски. В противном случае поведение не определено. Если ваше представление уже находится в иерархии, вам необходимо удалить его из суперпредставления, замаскировать его, а затем вернуть на место.
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];
Из-за того, как работает рендеринг Core Animation, маскирование - относительно медленная операция. Каждая маска требует дополнительного прохода рендеринга. Так что используйте маски экономно.
Одна из лучших частей этого подхода заключается в том, что вам больше не нужно создавать custom
UIView
и overridedrawRect:
. Это должно сделать ваш код проще и, возможно, даже быстрее.источник
Я взял пример Натана и создал категорию,
UIView
позволяющую придерживаться принципов DRY. Без дальнейших церемоний: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
Звонить:
[myView addRoundedCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight withRadii:CGSizeMake(20.0f, 20.0f)];
источник
Чтобы немного расширить ответ PL, я переписал метод так, чтобы он не округлял определенные объекты, например
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)];
источник
Если вы хотите сделать это в Swift, вы можете использовать расширение
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.maskedCorners недоступен. Итак, мы можем сделать это
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 } }
Мы написали maskByRoundingCorners как расширение UIView, чтобы улучшить повторное использование кода.
Благодарности @SachinVsSachin и @PL :) Я объединил их коды, чтобы сделать это лучше.
источник
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)];
измените "AllCorners" в соответствии с вашими потребностями.
источник
Все представленные решения достигают цели. Но
UIConstraints
иногда это может взорвать.Выделение:
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:roundedView.bounds byRoundingCorners: (UIRectCornerTopRight | UIRectCornerBottomRight) cornerRadii:CGSizeMake(16.0, 16.0)];
Приведенный выше фрагмент кода будет закруглен только в правом верхнем углу, если этот код установлен
viewDidLoad
. ПотомуroundedView.bounds
что он изменится после обновления ограниченийUIView
.источник
Создайте маску и установите ее на слой представления.
источник
Начиная со своего кода, вы можете использовать что-то вроде фрагмента ниже.
Я не уверен, что вам нужен именно такой результат. Также стоит отметить, что если / когда система снова вызывает drawRect: с запросом на перерисовку только части прямоугольника, это будет вести себя очень странно. Подход Невана , упомянутый выше, может быть лучшим выходом.
// 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); }
источник
-drawRect:
влияет только на вид, который вы рисуете. Его подвиды отрисовываются независимо после завершения рисования.Слегка хакерский, но относительно простой (без подклассов, маскирования и т. Д.) Способ - иметь два UIView. Оба с
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];
источник
Решение 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]; }
источник
Это может работать, только если некоторые вещи настроены правильно:
источник
Я понимаю, что вы пытаетесь округлить два верхних угла UITableView, но по какой-то причине я обнаружил, что лучшим решением является использование:
self.tableView.layer.cornerRadius = 10;
Программно он должен закруглить все четыре угла, но по какой-то причине округляет только два верхних. ** Пожалуйста, посмотрите снимок экрана ниже, чтобы увидеть эффект от кода, который я написал выше.
Надеюсь, это поможет!
источник
UITableView
то в другом месте должен быть код, который заставляетUITableView
не иметь закругленных углов внизу, или, может быть, дажеUIView
илиUILabel
, закрывающий нижнюю частьUITableView
.Вам, вероятно, придется обрезаться до предела. Добавьте строку
self.clipsToBounds = YES
где-нибудь в коде, чтобы установить это свойство.
источник