Округленный 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]];
Над кодом применяется округлость к каждому из углов поставляемого View. Вместо этого я просто хотел применить округлость к выбранным углам, например: top/top + left/bottom + right и т.д.
Возможно ли это? Как?
Ответы
Ответ 1
Я использовал ответ в Как создать круглый угол UILabel на iPhone? и код из How представляет собой округленное прямоугольное представление с прозрачностью, сделанной на iphone?, чтобы сделать этот код.
Тогда я понял, что ответил на неправильный вопрос (дал округленный UILabel вместо UIImage), поэтому я использовал этот код для его изменения:
http://discussions.apple.com/thread.jspa?threadID=1683876
Сделайте проект iPhone с помощью шаблона View. В контроллере представления добавьте следующее:
- (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
и удалив некоторые значения радиуса в коде. Код для всех углов есть, поэтому используйте это как отправную точку и удалите части, которые создают углы, которые вам не нужны. Обратите внимание, что вы также можете сделать прямоугольники с 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 text http://nevan.net/skitch/skitched-20100224-092237.png
Не забывайте, что для этого вам понадобится структура QuartzCore.
Ответ 2
Начиная с iOS 3.2, вы можете использовать функциональные возможности UIBezierPath
для создания прямоугольного прямоугольного прямоугольника (в котором только углы, которые вы указываете, закруглены). Затем вы можете использовать это как путь к 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 layer
imageView.layer.mask = maskLayer;
И что он - не вмешивается вручную, определяя фигуры в Core Graphics, не создавая маскирующие изображения в Photoshop. Слой даже не требует аннулирования. Применение округленного угла или переход в новый угол так же просто, как определение нового UIBezierPath
и использование его CGPath
в качестве пути слоя маски. Параметр corners
метода bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
является битовой маской, и поэтому множество углов можно закруглять, объединив их.
EDIT: добавление тени
Если вы хотите добавить тень к этому, потребуется немного больше работы.
Поскольку "imageView.layer.mask = maskLayer
" применяет маску, тень обычно не отображается за ее пределами. Фокус в том, чтобы использовать прозрачный вид, а затем добавить два подслоя (CALayer
s) на уровень представления: shadowLayer
и roundedLayer
. Оба должны использовать 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];
Ответ 3
Я использовал этот код во многих местах моего кода, и он работает на 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];
Ответ 4
В iOS 11 теперь мы можем закруглять только некоторые углы
let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
Ответ 5
Расширение 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))
Ответ 6
Пример 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 layer
imageview.layer.mask = maskLayer;
Ответ 7
Спасибо за обмен. Здесь я хотел бы поделиться решением для быстрого 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
Ответ 8
есть более простой и быстрый ответ, который может работать в зависимости от ваших потребностей, а также работает с тенями. вы можете установить maskToBounds на суперслое в true и смещать дочерние слои так, чтобы 2 их угла были за пределами границ суперслоя, эффективно разрезая закругленные углы с двух сторон.
конечно, это работает только тогда, когда вы хотите иметь только 2 закругленных угла на одной стороне, а содержимое слоя выглядит одинаково, когда вы отрезаете несколько пикселей с одной стороны. отлично подходит для того, чтобы гистограммы были закруглены только на верхней стороне.
Ответ 9
Смотрите этот связанный вопрос. Вам нужно будет нарисовать свой прямоугольник до CGPath
с закругленными углами, добавить CGPath
в CGContext
, а затем кликнуть его с помощью CGContextClip
.
Вы также можете нарисовать закругленный прямоугольник с альфа-значениями для изображения, а затем использовать это изображение для создания нового слоя, который вы установили в качестве свойства слоя mask
(см. документацию Apple).
Ответ 10
Округление только в некоторых углах не будет хорошо сочетаться с автоматическим изменением размера или автоматической компоновкой.
Итак, еще один вариант - использовать обычный cornerRadius
и скрыть углы, которые вы не хотите, под другим видом или за пределами его границ надзора, чтобы убедиться, что он настроен на клип его содержимого.
Ответ 11
Половина десятилетия поздно, но я думаю, что нынешний способ сделать это не на 100% прав. У многих людей возникла проблема с тем, что использование метода UIBezierPath + CAShapeLayer мешает Auto-layout, особенно когда он установлен на Storyboard. Никаких ответов не осталось, поэтому я решил добавить свои собственные.
Существует очень простой способ обойти это: нарисуйте закругленные углы в функции 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
}
}
Это лучший способ преодолеть проблему и не требует никакого времени для адаптации.
Ответ 12
Чтобы добавить к ответ и , я создал простую, многоразовую 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)
}
}
Ответ 13
Завершая ответ Стюарта, вы можете использовать метод округления в следующем виде:
@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];
Ответ 14
Я бы предложил определить маску слоя. Сама маска должна быть объектом 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
}
}