Reputation: 437
Hi Im wondering if It's possible to set a corner radius on a UIVisualEffectView? Here's the code that i've tried:
@IBOutlet var blurView: UIVisualEffectView!
var blurLayer : CALayer{
return blurView.layer
}
override func viewDidLoad() {
super.viewDidLoad()
setUpLayer()
// Do any additional setup after loading the view.
}
func setUpLayer(){
blurLayer.cornerRadius = 50
}
and
@IBOutlet var blurView: UIVisualEffectView!
override func viewDidLoad() {
super.viewDidLoad()
blurView.layer.cornerRadius = 50
// Do any additional setup after loading the view.
}
Non of them works.
Upvotes: 39
Views: 14520
Reputation: 91
in storyboard add two parameter in user-defined runtime attributes layer.cornerRadius = 8
and layer.masksToBounds = true
or in code
@IBOutlet var blurView: UIVisualEffectView! {
didSet {
blurView.layer.cornerRadius = 8
blurView.layer.masksToBounds = true
}
}
Upvotes: 9
Reputation: 3556
All existed solutions are not perfect.
Since UIVisualEffectView
implements its visual effects by compositing two subviews contents - one is a backdrop view and the other one is a filter view, when you implementing your corner radius by masking the whole UIVisualEffectView
, there would be some dirty colors around corners.
To get rid of these dirty colors, you just need to mask UIVisualEffects's filter view.
private typealias ObjcRawUIVisualEffectViewSelCGRect =
@convention(c) (UIVisualEffectView, Selector, CGRect) -> Void
private var cornerRadiusKey =
"com.WeZZard.Waxing.UIVisualEffectView-CornerRadius.cornerRadius"
private var needsUpdateMaskLayerKey =
"com.WeZZard.Waxing.UIVisualEffectView-CornerRadius.needsUpdateMaskLayer"
extension UIVisualEffectView {
public var cornerRadius: CGFloat {
get {
if let storedValue = objc_getAssociatedObject(self,
&cornerRadiusKey)
as? CGFloat
{
return storedValue
}
return 0
}
set {
if cornerRadius != newValue {
objc_setAssociatedObject(self,
&cornerRadiusKey,
newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
setNeedsUpdateMaskLayer()
}
}
}
private var needsUpdateMaskLayer: Bool {
get {
if let storedValue = objc_getAssociatedObject(self,
&needsUpdateMaskLayerKey)
as? Bool
{
return storedValue
}
return false
}
set {
objc_setAssociatedObject(self,
&needsUpdateMaskLayerKey,
newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
public override class func initialize() {
swizzle_setBounds()
}
private class func swizzle_setBounds() {
struct Static {
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
let selector: Selector = "setBounds:"
let method = class_getInstanceMethod(self, selector)
let imp_original = method_getImplementation(method)
before_setBounds = unsafeBitCast(imp_original,
ObjcRawUIVisualEffectViewSelCGRect.self)
class_replaceMethod(self,
selector,
unsafeBitCast(after_setBounds, IMP.self),
"@:{_struct=CGRect}")
}
}
private func setNeedsUpdateMaskLayer() {
needsUpdateMaskLayer = true
NSOperationQueue.mainQueue().addOperationWithBlock { [weak self] _ in
self?.updateMaskLayerIfNeeded()
}
}
private func updateMaskLayerIfNeeded() {
if needsUpdateMaskLayer {
updateMaskLayer()
needsUpdateMaskLayer = false
}
}
private func updateMaskLayer(){
var filterViewFound = false
for each in subviews {
if each.dynamicType.description()
.containsString("Filter")
{
filterViewFound = true
let newPath = UIBezierPath(roundedRect: each.bounds,
cornerRadius: self.cornerRadius)
.CGPath
if let existedMask = each.layer.mask
as? CAShapeLayer
{
existedMask.path = newPath
} else {
let shapeLayer = CAShapeLayer()
shapeLayer.path = newPath
each.layer.mask = shapeLayer
}
} else {
setNeedsUpdateMaskLayer()
}
}
assert(filterViewFound == true, "Filter view was not found! Check your hacking!")
}
}
private var before_setBounds: ObjcRawUIVisualEffectViewSelCGRect = { _ in
fatalError("No implementation found")
}
private let after_setBounds: ObjcRawUIVisualEffectViewSelCGRect = {
(aSelf, selector, bounds) -> Void in
let oldBounds = aSelf.bounds
before_setBounds(aSelf, selector, bounds)
if oldBounds.size != bounds.size {
aSelf.setNeedsUpdateMaskLayer()
}
}
And all things done!
Upvotes: 5
Reputation: 5132
After @theMonster suggestion, I am posting what was a comment.
override func viewDidLoad() {
super.viewDidLoad()
blurView.layer.cornerRadius = 50
blurView.clipsToBounds = true
}
Upvotes: 67
Reputation: 12569
Subclass UIVisualEffectView
class PSORoundedVisualEffectView : UIVisualEffectView{
override func layoutSubviews() {
super.layoutSubviews()
updateMaskLayer()
}
func updateMaskLayer(){
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 10).CGPath
self.layer.mask = shapeLayer
}
}
Replace the UIBezierPath with whatever shape you want.
Upvotes: 6