在我的应用中,我有UIView
正在使用CALayer
来实现阴影:
@implementation MyUIView
- (instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(!self) return self;
self.layer.shadowColor = [UIColor colorWithWhite:0 alpha:.2].CGColor;
self.layer.shadowOffset = CGSizeMake(0, 2);
self.layer.shadowOpacity = 1;
self.layer.shadowRadius = 1;
return self;
}
@end
如果我想要任何接近合理性能的内容,我必须定义CALayer
' s shadowPath
:
@implementation MyUIView
- (void) setFrame:(CGRect)frame {
[super setFrame:frame];
self.layer.shadowPath = CGPathCreateWithRect(self.bounds, NULL);
}
@end
每当我为UIView
设置动画时,我都注意到了两件事:
如果我不使用shadowPath
,阴影会随着旋转和帧大小的变化而动画效果很好。这里需要注意的是动画非常慢,而且表现普遍不足。
如果我执行,只要shadowPath
动画处理UIView
,动画就会流畅而及时,但阴影的过渡本身就很多比没有shadowPath
更像块(和更不平滑)。
示例:
shadowPath
:https://gfycat.com/ColdBlissfulIndusriverdolphin(请注意阴影的行为方式如同变形不足的矩形?)shadowPath
(动画的缓慢在设备上更明显,但你明白了):https://gfycat.com/ActiveRemorsefulBandicoot 修改
值得注意的是,这些动画是隐含的 - 我自己并没有调用它们。它们是UIViewController
随设备方向旋转的结果。阴影位于UIView
上,在旋转期间会改变大小。
答案 0 :(得分:4)
我尝试重现您提供的两个GIF中显示的行为,但没有成功(也许您可以使用动画代码编辑您的问题,例如UIView animateWithDuration:animations:
)。
然而,在我脑海中的某个地方,我记得有一段时间我遇到了类似的问题,结果发现我必须光栅化视图以使其平滑。
所以我无法保证它能为您解决问题,但请试一试:
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [[UIScreen mainScreen] scale];
答案 1 :(得分:0)
在调整视图大小时,shadowPath 需要设置额外的动画。
您可以直接使用我的课程。
/*
Shadow.swift
Copyright © 2018, 2020-2021 BB9z
https://github.com/BB9z/iOS-Project-Template
The MIT License
https://opensource.org/licenses/MIT
*/
/**
A view drops shadow.
*/
@IBDesignable
class ShadowView: UIView {
@IBInspectable var shadowOffset: CGPoint = CGPoint(x: 0, y: 8) {
didSet { needsUpdateStyle = true }
}
@IBInspectable var shadowBlur: CGFloat = 10 {
didSet { needsUpdateStyle = true }
}
@IBInspectable var shadowSpread: CGFloat = 0 {
didSet { needsUpdateStyle = true }
}
/// Set nil can disable shadow
@IBInspectable var shadowColor: UIColor? = UIColor.black.withAlphaComponent(0.3) {
didSet { needsUpdateStyle = true }
}
@IBInspectable var cornerRadius: CGFloat {
get { layer.cornerRadius }
set { layer.cornerRadius = newValue }
}
private var needsUpdateStyle = false {
didSet {
guard needsUpdateStyle, !oldValue else { return }
DispatchQueue.main.async { [self] in
if needsUpdateStyle { updateLayerStyle() }
}
}
}
private func updateLayerStyle() {
needsUpdateStyle = false
if let color = shadowColor {
Shadow(view: self, offset: shadowOffset, blur: shadowBlur, spread: shadowSpread, color: color, cornerRadius: cornerRadius)
} else {
layer.shadowColor = nil
layer.shadowPath = nil
layer.shadowOpacity = 0
}
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
updateLayerStyle()
}
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
lastLayerSize = layer.bounds.size
if shadowColor != nil, layer.shadowOpacity == 0 {
updateLayerStyle()
}
}
private var lastLayerSize = CGSize.zero {
didSet {
if oldValue == lastLayerSize { return }
guard shadowColor != nil else { return }
updateShadowPathWithAnimationFixes(bonuds: layer.bounds)
}
}
// We needs some additional step to achieve smooth result when view resizing
private func updateShadowPathWithAnimationFixes(bonuds: CGRect) {
let rect = bonuds.insetBy(dx: shadowSpread, dy: shadowSpread)
let newShadowPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
if let resizeAnimation = layer.animation(forKey: "bounds.size") {
let key = #keyPath(CALayer.shadowPath)
let shadowAnimation = CABasicAnimation(keyPath: key)
shadowAnimation.duration = resizeAnimation.duration
shadowAnimation.timingFunction = resizeAnimation.timingFunction
shadowAnimation.fromValue = layer.shadowPath
shadowAnimation.toValue = newShadowPath
layer.add(shadowAnimation, forKey: key)
}
layer.shadowPath = newShadowPath
}
}
/**
Make shadow with the same effect as Sketch app.
*/
func Shadow(view: UIView?, offset: CGPoint, blur: CGFloat, spread: CGFloat, color: UIColor, cornerRadius: CGFloat = 0) { // swiftlint:disable:this identifier_name
guard let layer = view?.layer else {
return
}
layer.shadowColor = color.cgColor
layer.shadowOffset = CGSize(width: offset.x, height: offset.y)
layer.shadowRadius = blur
layer.shadowOpacity = 1
layer.cornerRadius = cornerRadius
let rect = layer.bounds.insetBy(dx: spread, dy: spread)
layer.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
}
通过https://github.com/BB9z/iOS-Project-Template/blob/master/App/General/Effect/Shadow.swift