我在一个应用程序上工作了几年,收到了一个简单的设计请求:在UIView上圆角并添加投影。做以下所给的事
我想要一个自定义的UIView…:我只是想要一个空白的白色视图与圆角和光滴阴影(没有照明效果)。我可以逐个执行这些操作,但通常会发生clipToBounds/maskToBounds冲突。
我在一个应用程序上工作了几年,收到了一个简单的设计请求:在UIView上圆角并添加投影。做以下所给的事
我想要一个自定义的UIView…:我只是想要一个空白的白色视图与圆角和光滴阴影(没有照明效果)。我可以逐个执行这些操作,但通常会发生clipToBounds/maskToBounds冲突。
当前回答
iOS阴影和拐角半径
[iOS CALayer]
[iOS masksToBounds]
[iOS调试渲染]
你可以使用图层设置阴影
view1.layer.shadowColor = UIColor.magenta.cgColor
view1.layer.shadowOffset = CGSize(width: 0, height: 0)
view1.layer.shadowOpacity = 1
view1.layer.shadowRadius = 0
可视化
1. shadowoffset。宽度,2. shadowoffset。高度,3。shadowOpacity 4。shadowRadius
shadowOffset宽度和高度是任意的 shadowOpacity从0到1 shadowRadius从0开始为正
不是简单的任务
请注意,阴影不是仅根据边界和边角raduis计算的。在创建阴影的过程中,需要考虑以下事项:
子视图层 子层 内容(支持图像)
view1.backgroundColor = .clear
view1.layer.contents = UIImage(named: "ring")?.cgImage
view1.layer.contentsScale = UIScreen.main.scale
ScaleFactor(contentsScale, rasterizationScale) -默认为1.0
currentBitmapSize = layerSize * scaleFactor
//non retina
1 point == 1x pixels
//Retina
1 point == 2x pixels
//or
1 point == 3x pixels
//For example to draw line
point(width: 4, height: 2) == 1x pixels(width: 4, height: 2)(8 pixels) == 2x pixels(width: 8, height: 4)(32 pixels)
使用uisscreen .main.scale =当前屏幕的缩放因子
[iOS像素、点数、单位]
的绩效
使用层。角半径,阴影有一些性能影响
至于层。cornerRadius性能:
应用它的颜色混合使用[阅读更多]
至于影子Xcode提示你:
The layer is using dynamic shadows which are expensive to render. If possible try setting shadowPath, or pre-rendering the shadow into an image and putting it under the layer
1. 另外使用shadowPath
用于内部静态层。默认情况下,它是nil,这就是为什么UIKit应该创建一个屏幕外视图,并基于这个信息创建一个阴影。这就是为什么您能够预定义路径并进行设置。另一个优点是,您可以根据需要创建自定义阴影
view1.layer.shadowPath = UIBezierPath(roundedRect: view1.bounds, cornerRadius: 50).cgPath
缺点-缺乏活力。如果视图改变边界(宽度,高度,角半径…),阴影仍然是它是(旧的边界)。如果视图的位置被改变(移动,滚动),shadowPath将是正确的
2. 缓存栅格化
(iOS shouldRasterize)
其他回答
我在UIView中创建了一个helper
@interface UIView (Helper)
- (void)roundCornerswithRadius:(float)cornerRadius
andShadowOffset:(float)shadowOffset;
@end
你可以这样叫它
[self.view roundCornerswithRadius:5 andShadowOffset:5];
这是实现
- (void)roundCornerswithRadius:(float)cornerRadius
andShadowOffset:(float)shadowOffset
{
const float CORNER_RADIUS = cornerRadius;
const float SHADOW_OFFSET = shadowOffset;
const float SHADOW_OPACITY = 0.5;
const float SHADOW_RADIUS = 3.0;
UIView *superView = self.superview;
CGRect oldBackgroundFrame = self.frame;
[self removeFromSuperview];
CGRect frameForShadowView = CGRectMake(0, 0, oldBackgroundFrame.size.width, oldBackgroundFrame.size.height);
UIView *shadowView = [[UIView alloc] initWithFrame:frameForShadowView];
[shadowView.layer setShadowOpacity:SHADOW_OPACITY];
[shadowView.layer setShadowRadius:SHADOW_RADIUS];
[shadowView.layer setShadowOffset:CGSizeMake(SHADOW_OFFSET, SHADOW_OFFSET)];
[self.layer setCornerRadius:CORNER_RADIUS];
[self.layer setMasksToBounds:YES];
[shadowView addSubview:self];
[superView addSubview:shadowView];
}
这对我很管用。窍门是将背景颜色从主视图移到图层中。
CALayer *layer = view.layer;
layer.cornerRadius = 15.0f;
layer.masksToBounds = NO;
layer.shadowOffset = CGSizeMake(0, 3);
layer.shadowColor = [[UIColor blackColor] CGColor];
layer.shadowRadius = 2.0f;
layer.shadowOpacity = 0.35f;
layer.shadowPath = [[UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:layer.cornerRadius] CGPath];
CGColorRef bColor = view.backgroundColor.CGColor;
view.backgroundColor = nil;
layer.backgroundColor = bColor ;
你需要使用两个uiview来实现这一点。一个UIView会像阴影一样工作,另一个会为圆角边界工作。
下面是一个在协议帮助下的类方法的代码片段:
@implementation UIMethods
+ (UIView *)genComposeButton:(UIViewController <UIComposeButtonDelegate> *)observer;
{
UIView *shadow = [[UIView alloc]init];
shadow.layer.cornerRadius = 5.0;
shadow.layer.shadowColor = [[UIColor blackColor] CGColor];
shadow.layer.shadowOpacity = 1.0;
shadow.layer.shadowRadius = 10.0;
shadow.layer.shadowOffset = CGSizeMake(0.0f, -0.5f);
UIButton *btnCompose = [[UIButton alloc]initWithFrame:CGRectMake(0, 0,60, 60)];
[btnCompose setUserInteractionEnabled:YES];
btnCompose.layer.cornerRadius = 30;
btnCompose.layer.masksToBounds = YES;
[btnCompose setImage:[UIImage imageNamed:@"60x60"] forState:UIControlStateNormal];
[btnCompose addTarget:observer action:@selector(btnCompose_click:) forControlEvents:UIControlEventTouchUpInside];
[shadow addSubview:btnCompose];
return shadow;
}
在上面的代码中,btnCompose_click:将成为一个@required委托方法,该方法将在单击按钮时触发。
在这里我添加了一个按钮到我的UIViewController,像这样:
UIView *btnCompose = [UIMethods genComposeButton:self];
btnCompose.frame = CGRectMake(self.view.frame.size.width - 75,
self.view.frame.size.height - 75,
60, 60);
[self.view addSubview:btnCompose];
结果如下所示:
这里是masksToBounds冲突问题的解决方案,它适用于我。
在你设置corderRadius/borderColor/shadow等之后,将masksToBounds设置为NO:
v.layer.masksToBounds = NO;
斯威夫特
// corner radius
blueView.layer.cornerRadius = 10
// border
blueView.layer.borderWidth = 1.0
blueView.layer.borderColor = UIColor.black.cgColor
// shadow
blueView.layer.shadowColor = UIColor.black.cgColor
blueView.layer.shadowOffset = CGSize(width: 3, height: 3)
blueView.layer.shadowOpacity = 0.7
blueView.layer.shadowRadius = 4.0
探索选择
问题1:阴影被剪掉了
如果我们希望将子层或子视图(如图像)的内容剪辑到视图的边界,该怎么办?
我们可以用
blueView.layer.masksToBounds = true
(另外,blueView。clipsToBounds = true给出相同的结果。)
但是,哦不!影子也被剪掉了,因为它在边界之外!怎么办呢?怎么办呢?
解决方案
阴影和边界使用单独的视图。基本视图是透明的,并且有阴影。边界视图将它拥有的任何其他子内容剪辑到它的边界。
// add the shadow to the base view
baseView.backgroundColor = UIColor.clear
baseView.layer.shadowColor = UIColor.black.cgColor
baseView.layer.shadowOffset = CGSize(width: 3, height: 3)
baseView.layer.shadowOpacity = 0.7
baseView.layer.shadowRadius = 4.0
// add the border to subview
let borderView = UIView()
borderView.frame = baseView.bounds
borderView.layer.cornerRadius = 10
borderView.layer.borderColor = UIColor.black.cgColor
borderView.layer.borderWidth = 1.0
borderView.layer.masksToBounds = true
baseView.addSubview(borderView)
// add any other subcontent that you want clipped
let otherSubContent = UIImageView()
otherSubContent.image = UIImage(named: "lion")
otherSubContent.frame = borderView.bounds
borderView.addSubview(otherSubContent)
结果如下:
问题2:表现不佳
添加圆角和阴影可以提高性能。您可以通过为阴影使用预定义的路径并指定它是栅格化来提高性能。下面的代码可以添加到上面的示例中。
baseView.layer.shadowPath = UIBezierPath(roundedRect: baseView.bounds, cornerRadius: 10).cgPath
baseView.layer.shouldRasterize = true
baseView.layer.rasterizationScale = UIScreen.main.scale
更多细节请看这篇文章。看这里和这里。
我们用Swift 4和Xcode 9测试了这个答案。