问题
I have a scroll view that I'm using to control the alpha of other elements depending on how far down the view the user scrolls.
First, I set up the blur view. Alpha here doesn't seem to take, firstly
var effect: UIBlurEffectStyle = .light
if #available(iOS 10.0, *) {
effect = .prominent
}
let blurView = UIVisualEffectView(effect: UIBlurEffect(style: effect))
blurView.alpha = 0
if let navigationBar = navigationController?.navigationBar {
blurView.frame = navigationBar.bounds
}
self.blurView = blurView
Then in my UIScrollViewDelegate
:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var alpha = (scrollView.contentOffset.y / 200)
alpha = alpha > 1.0 ? 1.0 : alpha
blurView?.alpha = alpha
}
This doesn't seem to be working either.
Any advice on how to achieve this effect? Thanks!
回答1:
When you read the Apple documentation you can find:
When using the UIVisualEffectView class, avoid alpha values that are less than 1.
In fact you should obtain also a warning:
[Warning] <UIVisualEffectView 0x7af7a3b0> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
P.S.: Pay attention, what I mean is that you can change the alpha of the visual effects but the view could appear as simply partially transparent rather than blurred.
If you don't believe in this documentation, you can test my little example below to a blank project and you can see that, if you change alpha value, your effect stop to work correctly:
(You can find all the project below to my GitHUB repository here)
Prepare in a blank project a navigationController connected to a viewController like this:
Put a generic image with an UIImageView
to the view of your viewController, like this image:
class ViewController: UIViewController {
var blurView: UIVisualEffectView!
var effect: UIBlurEffectStyle = .light
var blurEffect : UIBlurEffect!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// This code make more transparent our navigation
if let navigationBar = navigationController?.navigationBar {
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
navigationBar.tintColor = .white
let textAttributes = [NSForegroundColorAttributeName:UIColor.white]
navigationBar.titleTextAttributes = textAttributes
navigationBar.shadowImage = UIImage()
navigationBar.backgroundColor = UIColor(red: 0.0, green: 0.3, blue: 0.5, alpha: 0.3)
navigationBar.isTranslucent = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 10.0, *) {
effect = .prominent
} else {
// Fallback on earlier versions
}
if let navigationBar = navigationController?.navigationBar {
let noEffectView = UIVisualEffectView.init(frame: navigationBar.bounds)
self.blurEffect = UIBlurEffect(style: effect)
self.blurView = noEffectView
navigationBar.addSubview(self.blurView)
// This line below to don't blur buttons and title
navigationBar.sendSubview(toBack: self.blurView)
// Apply the effect:
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.applyBlur), userInfo: nil, repeats: false)
}
}
func applyBlur() {
print("Apply the blur effect..")
UIView.animate(withDuration: 10.2, animations: {
self.blurView.effect = self.blurEffect
}, completion:{ (finished: Bool) in
// Make test with a simple timer:
Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(self.changeAlpha), userInfo: nil, repeats: true)
})
}
func changeAlpha() {
let startNum:CGFloat = 0.0; let stopNum:CGFloat = 200.0
let randomNum = CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(startNum - stopNum) + min(startNum, stopNum)
var alpha = (randomNum / 200)
alpha = alpha > 1.0 ? 1.0 : alpha
print("we change alpha to : \(alpha)")
self.blurView.alpha = alpha
}
}
Output with the blur effect:
Output during alpha changes:
A solution:
Probably you don't want to change the alpha value but the amount of blur effect during the scrolling so the solution could be to use the new fractionComplete
of UIViewPropertyAnimator
property available only from iOS 10.0 (as suggested in this other SO answer)
import UIKit
@available(iOS 10.0, *)
class ViewController: UIViewController {
var blurView: UIVisualEffectView!
var effect: UIBlurEffectStyle = .light
var blurEffect : UIBlurEffect!
var animator: UIViewPropertyAnimator!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// This code make more transparent our navigation
if let navigationBar = navigationController?.navigationBar {
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
navigationBar.tintColor = .white
let textAttributes = [NSForegroundColorAttributeName:UIColor.white]
navigationBar.titleTextAttributes = textAttributes
navigationBar.shadowImage = UIImage()
navigationBar.backgroundColor = UIColor(red: 0.0, green: 0.3, blue: 0.5, alpha: 0.3)
navigationBar.isTranslucent = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
effect = .prominent
if let navigationBar = navigationController?.navigationBar {
let noEffectView = UIVisualEffectView.init(frame: navigationBar.bounds)
self.blurEffect = UIBlurEffect(style: effect)
self.blurView = noEffectView
navigationBar.addSubview(self.blurView)
// This line below to don't blur buttons and title
navigationBar.sendSubview(toBack: self.blurView)
// Apply the effect:
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.applyBlur), userInfo: nil, repeats: false)
}
}
func applyBlur() {
print("Apply the blur effect..")
animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear)
self.blurView.effect = self.blurEffect
animator.addAnimations {
self.blurView.effect = nil
}
Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(self.changeBlurFraction), userInfo: nil, repeats: true)
}
func changeBlurFraction() {
let startNum:CGFloat = 0.0; let stopNum:CGFloat = 200.0
let randomNum = CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(startNum - stopNum) + min(startNum, stopNum)
var blurFraction = (randomNum / 200)
blurFraction = blurFraction > 1.0 ? 1.0 : blurFraction
print("we change the blur fraction to : \(blurFraction)")
animator.fractionComplete = blurFraction
}
}
As you can see this change the blur effect not the alpha and every time the timer is fired you can see a different value of blur to your navigationBar.
The trick is to apply the blur effect and add a new animation to report the effect to nil but everytime with a new "fraction" to change the animation to that exact value of blur.
Output:
回答2:
Swift 5.0. Just use code below:
navigationController?.navigationBar.isTranslucent = false
It changes '0.85 alpha effect' with solid color with alpha = 1.
回答3:
Alpha blending of a UIVisualEffectView
is broken as of iOS 10, along with layer masking, due to internal change to the implementation by Apple.
I'd suggest using a UIViewPropertyAnimator
object to transition from the actual effect (.prominent
, in your case) to nil
, and then manually set the fractionComplete
to be the alpha value. It will give you a much better effect and allow you to interactively change the effect.
来源:https://stackoverflow.com/questions/43099786/how-do-you-programmatically-change-the-alpha-of-a-uivisualeffectview-in-a-naviga