I'm writing a SpriteKit game and faced a problem with blurred view, which lies on the SKView. It is supposed to slide from the right when game is paused and it should blur the content of it's parent view (SKView) just like control center panel in iOS 7. Here is the desired appearance:
What I actually get is:
In fact the view on the left is not totally black, you can see how highlights from the superview are slightly struggling through almost opaque subview, but no blur is applied. Is it an iOS 8 bug/feature, or is it my mistake/misunderstanding
Here is my UIVisualEffectView subclass's essensials:
class OptionsView: UIVisualEffectView {
//...
init(size: CGSize) {
buttons = [UIButton]()
super.init(effect: UIBlurEffect(style: .Dark))
frame = CGRectMake(-size.width, 0, size.width, size.height)
addButtons()
clipsToBounds = true
}
func show() {
UIView.animateWithDuration(0.3, animations: {
self.frame.origin.x = 0
})
}
func hide() {
UIView.animateWithDuration(0.3, animations: {
self.frame.origin.x = -self.frame.size.width
})
}
Then in GameScene class:
in initializer:
optionsView = OptionsView(size: CGSizeMake(130, size.height))
in didMoveToView(view: SKView):
view.addSubview(optionsView)
on pressing pause button:
self.optionsView.show()
P.S. Though I know two another ways to implement blur view, I thought this one was the easiest, since my app is going to support iOS8 only
Render a blurred static image from superview -> put UIImageView on the OptionsView, with clipsToBounds = true -> animate UIImageView position while animating optionsView position, so that blur stays still relatively to the superview
Forget about UIView, UIVisualEffectView and UIBlurView and use SKEffectNode together with SKCropNode.
Ok, I have managed to get the desired effect using SKEffectNode instead of UIVisualEffectView. Here is the code for someone facing the same issue
class BlurCropNode: SKCropNode {
var blurNode: BlurNode
var size: CGSize
init(size: CGSize) {
self.size = size
blurNode = BlurNode(radius: 10)
super.init()
addChild(blurNode)
let mask = SKSpriteNode (color: UIColor.blackColor(), size: size)
mask.anchorPoint = CGPoint.zeroPoint
maskNode = mask
}
}
class BlurNode: SKEffectNode {
var sprite: SKSpriteNode
var texture: SKTexture {
get { return sprite.texture }
set {
sprite.texture = newValue
let scale = UIScreen.mainScreen().scale
let textureSize = newValue.size()
sprite.size = CGSizeMake(textureSize.width/scale, textureSize.height/scale)
}
}
init(radius: CGFloat) {
sprite = SKSpriteNode()
super.init()
sprite.anchorPoint = CGPointMake(0, 0)
addChild(sprite)
filter = CIFilter(name: "CIGaussianBlur", withInputParameters: ["inputRadius": radius])
shouldEnableEffects = true
shouldRasterize = true
}
}
Result:
There are several issues though
Cropping doesn't work with SKEffectNode until it's shouldRasterize property is set to true. I get the whole screen blurred. So I still don't know how to properly implement realtime blur.
Animation on the BlurCropNode is not smooth enough. There is a lag at the beginning because of capturing texture and setting it to the effectNode's sprite child. Even dispatch_async doesn't help.
It would be very much appreciated if anyone could help to solve at least one of the problems
I know I'm probably a bit late but I was having the same problem and found a solution to creating a realtime blur on part of the screen. It's based on this tutorial: http://www.youtube.com/watch?v=eYHId0zgkdE where he used a shader to blur a static sprite. I extended his tutorial to capture of the part of the screen and then apply the blur to that. For your problem you could capture under that sidebar.
Firstly, you create an SKSpriteNode
to hold the captured texture. Then in didMoveToView()
you add your blur shader to that sprite. (You can find the blur.fsh file on GitHub, there's a link at the bottom of the youtube video.)
override func didMoveToView(view: SKView) {
blurNode.shader = SKShader(fileNamed: "blur")
self.addChild(blurNode)
}
Then you have to capture the section of the view you want to blur and apply the SKTexture
to, in my case, blurNode
.
override func update(currentTime: CFTimeInterval) {
// Hide the blurNode to avoid it be captured too.
blurNode.hidden = true
blurNode.texture = self.view!.textureFromNode(self, crop: blurNode.frame)
blurNode.hidden = false
}
And that should be it. On my iPad mini, with a blur of 1/3 of the width of the screen, the fps was 58-59. Blurring the whole screen the fps was down to about 22 so it's obviously not ideal for some things but hopefully it helps.
来源:https://stackoverflow.com/questions/24980353/uivisualeffectview-doesnt-blur-its-skview-superview