I have a View controller that features a full screen UIImageView. When the controller loads, I just set a blur effect over the whole screen. The content mode for the UIImageView is set to scaleAspectFill. I set the blur effect like this:
#IBOutlet weak var previewImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
previewImage.image = userPhoto
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
blur.frame = self.view.bounds
previewImage.addSubview(blur)
}
when I test the app on an iPhone 6 I get this result:
There's an offset on the right an I can't figure out why. The picture underneath if is properly full screened but I can get the blur effect to fit over it...
****EDITED****
I applied the blur effect to the VC's view directly and now it works just fine, but I'd like to know why it doesn't work when I add the subview to the UIImage View...
I see two possible problems here:
Views are not laid out on the viewDidLoad stage. In this case you can force them to layout manualy:
override func viewDidLoad() {
super.viewDidLoad()
self.view.layoutIfNeeded()
...
}
You add subview to the previewImage, not to the root view. You must check your storyboard, or fix code:
override func viewDidLoad() {
super.viewDidLoad()
previewImage.image = userPhoto
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
blur.frame = self.view.bounds
self.view.insertSubview(blur, aboveSubview: previewImage)
}
Based on #kelin answer. You can even add the blur effect above your image in the interface builder.Just search for Visual Effect View with Blur in the objects section in the interface builder. Then add the Visual Effect View view above your image. It is also fully customizable through Attributes inspector. That way you you make sure that the blur will occupy the entire screen. It is also cleaner from architecture perspective , you do not need to configure your view through code.
Related
After seeking a lot and trying many solutions, nope fixed my problem.
In my app I customized the UINavigationController in order to have blur effect:
import UIKit
class CustomNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
visualEffectView.frame = (self.navigationBar.bounds.insetBy(dx: 0, dy: -40).offsetBy(dx: 0, dy: -40))
self.navigationBar.isTranslucent = true
self.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationBar.addSubview(visualEffectView)
self.navigationBar.sendSubviewToBack(visualEffectView)
}
}
Then in Main.storyboard I selected the customized class for the navigation controller item.
The blur effect works properly, the status icons are correctly visible, but not the standard navigation bar items: left button, title and right button.
For a moment they appears but soon after the customized navigation bar covers them.
I'm using Xcode 12.4 and I'm running the app on iPhone Xr.
How can I show the navigation bar elements again?
Thanks a lot in advance.
Translucent navigation bars in iOS already blur the content behind the bar, so you shouldn't need to add a UIVisualEffectView nor set a backgroundImage.
If you modify your code to just:
override func viewDidLoad()
{
super.viewDidLoad()
self.navigationBar.isTranslucent = true
}
does this not achieve the visual effect you're looking for?
If not, please try the following adjustment to your methodology:
override func viewDidLoad()
{
super.viewDidLoad()
self.navigationBar.isTranslucent = true
// create a UIImageView
let backgroundImage: UIImageView = UIImageView()
backgroundImage.autoresizingMask = [.flexibleWidth, .flexibleHeight]
backgroundImage.image = UIImage()
// add a blur effect to the ImageView
let visualEffectView: UIVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
visualEffectView.frame = (self.navigationBar.bounds.insetBy(dx: 0, dy: -40).offsetBy(dx: 0, dy: -40))
backgroundImage.addSubview (visualEffectView)
// and set that as your backgroundImage on the navigationBar
self.navigationBar.setBackgroundImage(backgroundImage.image, for: .default)
}
this adds the blur effect to the backgroundImage. This seems to work for me, but the visual effect is no different than just using my first suggestion, likely because backgroundImage.image == nil.
This is certainly an improved approach in that it doesn't add unexpected subviews into the UINavigationBar view hierarchy, and I observed both methods did not affect the visibility of the bar controls.
I have a UITableViewController with a UIImageView. The content is loaded with the following simple code.
#IBOutlet weak var avatarImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
func updateUI() {
backendless.userService.currentUser.retrieveProperties()
avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width/2
avatarImageView.layer.masksToBounds = true
let imgURL = NSURL(string: (backendless.userService.currentUser.getProperty("Avatar") as? String)!)
avatarImageView.sd_setImageWithURL(imgURL)
tableView.reloadData()
}
The ImageView does not appear.
But if I delete the following code, the UIImageView will be set and displayed correctly.
avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width/2
avatarImageView.layer.masksToBounds = true
What am I missing? Help is very appreciated.:
PS:
Xcode 8.0
Swift 2.3
Contrains for ImageView are set to 120*120
sd_setImageWithURL = pod 'SDWebImage'
Try this:
override func viewDidLoad() {
super.viewDidLoad()
self.view.layoutIfNeeded() // <- This will update your initial view frames
updateUI()
}
What is happening is that in Xcode 8.0, by the time you hit viewDidLoad, the frame of all subviews of a controller is still x: 0.0, y: 0.0, w: 1000.0, h: 1000.0, so in effect you're making the corner radius of your avatar image view 500.0pt. When you call layout if needed, the correct frames will be calculated, and give you the value you're expecting.
One obvious problem is that you are calling updateUI too soon. In viewDidLoad, there is no UI. The view has loaded but that is all; it is not in the interface and no layout has yet taken place. Your code depends upon avatarImageView.frame, but we do not yet know what that is. You have constraints on the image view, but they have not yet taken effect! Thus you should postpone this call until after the interface is present, e.g. viewDidAppear or at least viewDidLayoutSubviews. But of course then you probably only want to call it once (typed in browser, not tested, but it will be something like this):
var didConfigureUI = false
func viewDidLayoutSubviews() {
if !didConfigureUI {
didConfigureUI = true
updateUI()
}
}
I think your problem with your controller. You have added the image view to a UITableViewController. That means constraint set to image view will not work if you use the frame in view did load. You need to change frame of the UIIMageView before use it or you need to use UIViewController. use the below code before using the frame of image view.
avatarImageView.frame.size.width = 120.0
avatarImageView.frame.size.height = 120.0
I think this will solve the problem. But you should use UIViewController if there is no special purpose of UITableViewController.
“xy.view.layoutIfNeeded()”
needs to be called everytime i’m calling a property before the view has finished loading
for example imageView.view.layoutIfNeeded() to call for the height and widths values
I have created a custom view class. I have a image view that I want to blur. I have added the blurring effect in the code below, but obviously it doesn't work because the bounds of the image view is wrong. Due to auto layout haven't calculated the correct sizes for height and width of the image view yet. However which method should I override to access correct position and size attributes?
And what would be the solution for only adding it once?
class ProductListingCell: UICollectionViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var sharpImageView: UIImageView!
#IBOutlet weak var blurImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
let blurEffect = UIBlurEffect(style: .Light)
let visualEffectView = UIVisualEffectView(effect: blurEffect)
visualEffectView.frame = blurImageView.bounds
blurImageView.addSubview(visualEffectView)
}
}
(edit) Maybe an answer:
Just after posting the question I found this method. Would this be the correct way to do it?
override func layoutSubviews() {
super.layoutSubviews()
layoutIfNeeded()
let blurEffect = UIBlurEffect(style: .Light)
let visualEffectView = UIVisualEffectView(effect: blurEffect)
visualEffectView.frame = blurImageView.bounds
blurImageView.addSubview(visualEffectView)
}
Yes, your are correct: layoutSubviews() is the way to go.
However, you don't need and actually shouldn't use layoutIfNeeded() inside the layoutSubviews() body. This is because what layoutIfNeeded() does is that checks if a particular internal flag is set on the view (it's set whenever you or the system call setNeedsLayout()) and if that's the case it calls layoutSubviews() on the view (the very same method from where you called it). With other words: You might create a never ending recursion cycle here if you do anything that invalidates the current layout in the layoutSubviews() method before calling layoutIfNeeded().
I tested your code with and without the layoutIfNeeded() call in a sample project and both implementations yield the same (correct) result. Please check if you made any other changes to your code.
When layoutSubviews() is called on your custom view you can be sure that the system has set the view's frame after resolving all layout constraints that constrain the view to its sibling views and its superview. However, you need to call super.layoutSubviews() first in order to properly position all of the view's subviews.
I have set a background image for my UITableViewController and then added a blur effect with the following code:
var blur = UIBlurEffect(style: UIBlurEffectStyle.Dark)
var blurView = UIVisualEffectView(effect: blur)
blurView.frame = self.view.bounds
self.view.insertSubview(blurView, atIndex: 0)
When I scroll down, the background image is still there but the blur effect is only there for the first few cells that fit into the view at a time. How can I make it so the blur effect is there as I scroll?
I tried adding the blur effect to each cell, but that makes it look really weird.
For UITableViewControllers self.view is the UITableView it self. That means any subview you add to self.view will scroll along with the content of the table view.
You could either make an own UIViewController, set up UITableView manually and place it in front of the blur view (instead of placing the blur view inside the table view) or you could move the blur view's location when the table view is scrolled.
To move the blur view to compensate for the table view's offset you can do something like this:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
var blurViewFrame = CGRect()
blurViewFrame.size = view.bounds.size
blurViewFrame.y = tableView.contentOffset.y
blurView.frame = blurViewFrame
}
I'm using UIBlurEffect from UIVisualEffectView to produce the look introduced in iOS 7. To do so I programmatically add anything I want portrayed on top of the blur to the contentView, as described in the UIBlurEffect Class Reference:
A UIBlurEffect object applies a blurring effect to the content layered
behind a UIVisualEffectView. Views added to the contentView of a
UIVisualEffectView are not affected by the blur effect.
But now I need to add a UIScrollView into the mix, and the fact that I had to use the contentView has left me completely confused. I'm not very familiar with the UIScrollView, but the iOS 8 Swift Programming Cookbook gives this example:
I'm not in need of displaying an imageView, but instead a separate view with many views inside of it in the hierarchy. Where exactly do I add the scrollView? Do I set scrollView as a subview of contentView? Or the other way around, the contentView as a subview of scrollView? Here's my current code:
class InfoVC: UIViewController {
#IBOutlet weak var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// blur
let blur = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.Light))
blur.frame = view.frame
view.addSubview(blur)
blur.contentView.addSubview(self.contentView)
}
You want to scroll something on top of the blur effect, correct?
In that case you need to add the scroll view to the content view of the blur view.
Assuming the contentView is the view you want to scroll it should work like this:
let blur = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.light))
blur.frame = view.bounds
view.addSubview(blur)
UIScrollView scrollView = UIScrollView(frame:blur.bounds)
blur.contentView.addSubview(scrollView)
scrollView.addSubview(contentView)
scrollView.contentSize = contentView.frame.size // or whatever other size you like