Today I added blur effect to navigationBar, which looks great. However the buttons inside navigationBar are only visible when I test the application on simulator but are hidden when I test it on real device(iPhone 5s). What could cause such a problem? I have no idea what causes this, maybe something is not in accordance with something?
I have two tableviews with navigation bar. I set the blur only in first view but it sets it also on the second view.
Edit: Inside first view all buttons are visible even on real device.
Edit2: All buttons are visible on real iPhone 6 IOS 9.3.4 but not on real iPhone 5s IOS 10 Beta 6. So the problem is only on IOS 10.
Edit3: The button is not visible but it acts right(goes back to first controller)
Like this I set the blur effect and call it inside viewDidLoad:
func addBlurEffect(toView view:UIView?) {
// Add blur view
guard let view = view else { return }
//This will let visualEffectView to work perfectly
if let navBar = view as? UINavigationBar{
navBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
navBar.shadowImage = UIImage()
}
var bounds = view.bounds
bounds.offsetInPlace(dx: 0.0, dy: -20.0)
bounds.size.height = bounds.height + 20.0
let blurEffect = UIBlurEffect(style: .Dark)
let visualEffectView = UIVisualEffectView(effect: blurEffect)
visualEffectView.userInteractionEnabled = false
visualEffectView.frame = bounds
visualEffectView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
view.insertSubview(visualEffectView, atIndex: 0)
}
Related
I have a weird problem. I'm trying to calculate the exact height between the bottom of the navigation bar and the top of the keyboard no matter which iOS device I'm running the app on. Here is the method where I'm doing this calculation:
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.height
let viewableArea = screenSize.height - keyboardRectangle.height - reportPostInstructionContainerViewHeight - characterCountContainerViewHeight - reportPostInstructionContainerViewHeight + 4
//iPhone 12 and above is "- 20"; iPhone 8 needs to be "+ 4"; iPhone 12 mini is "- 24"
print("**** navigationBarHeight: \(navigationBarHeight)")
print("**** keyboardHeight: \(keyboardHeight)")
print("**** screenSize.height: \(screenSize.height)")
print("**** total screen height - keyboard height: \(screenSize.height - keyboardRectangle.height)")
print("**** viewableArea: \(viewableArea)")
textViewHeight = viewableArea
print("**** textViewHeight: \(textViewHeight)")
UIView.animate(withDuration: 0.01, animations: { () -> Void in
self.textView.anchor(
top: self.horizontalDividerView.bottomAnchor,
leading: self.view.leadingAnchor,
bottom: nil,
trailing: self.view.trailingAnchor,
identifier: "ReportPostPFAVC.textView.directionalAnchors",
size: .init(width: 0, height: self.textViewHeight)
)
self.textView.layoutIfNeeded()
})
}
}
The line where "viewableArea" is being calculated seems to be the issue. For example, if I'm running the app on an iPhone 8, I need to add 4 to this calculation in order to size the text view properly.
Here is an image for reference:
I'm trying to get the bar with the "Report" button to sit perfectly on top of the keyboard. But, if I test on different devices sometimes I need to subtract 20 or 24 instead of adding 4.
I don't really understand where this gap is coming from.
Can anyone advise?
I will try to approach this from a different angle as I am not sure where exactly your issue is and what exactly was your logic from the code alone that you provided.
What you need to achieve is to find the coordinates of two frames in the same coordinate system. The two frames being; the keyboard frame and the navigation bar frame. And the "same coordinate system" is best defined by one of your views such as the view of your view controller.
There are convert methods on UIView which are designed to convert frames to/from different coordinate systems such as views.
So in your case all you need to do is
let targetView = self.view!
let convertedNavigationBarFrame = targetView.convert(self.navigationController!.navigationBar.bounds, from: self.navigationController!.navigationBar)
let convertedKeyboardFrame = targetView.convert(keyboardFrame.cgRectValue, from: nil)
In this example I used self.view as my coordinate system in which I want the two frames. This means the coordinates will be within a view controller. To get a height between two frames (which is your question) I could use absolutely any view that is in same window hierarchy and I should be getting the same result.
Then in this example I convert bounds of navigation bar from navigation bar, to target view. I found this to be best approach when dealing with UIView frames.
Last I convert keyboard frame to target view. The keyboard frame has a screen coordinate system which leads to using from: nil.
Getting the vertical distance between them is then a simple subtraction of two vertical coordinates
convertedKeyboardFrame.minY - convertedNavigationBarFrame.maxY
To have a full example I cerated a new project. In storyboard:
I added a navigation controller
I set the navigation controller to be "initial".
I set the old auto-generated view controller to be the root view controller of the navigation controller.
I added a text field which will trigger the
appearance of keyboard.
Then applied the following code:
The example code:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
self.navigationController?.navigationBar.backgroundColor = .green
}
private lazy var checkView: UIView = {
let checkView = UIView(frame: .zero)
checkView.backgroundColor = UIColor.black
checkView.layer.borderColor = UIColor.red.cgColor
checkView.layer.borderWidth = 5
self.view.addSubview(checkView)
return checkView
}()
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let targetView = self.view!
let convertedNavigationBarFrame = targetView.convert(self.navigationController!.navigationBar.bounds, from: self.navigationController!.navigationBar)
let convertedKeyboardFrame = targetView.convert(keyboardFrame.cgRectValue, from: nil)
checkView.frame = CGRect(x: 30.0, y: convertedNavigationBarFrame.maxY, width: 100.0, height: convertedKeyboardFrame.minY - convertedNavigationBarFrame.maxY)
}
}
}
The checkView appears between navigation bar and keyboard to show that the computation is correct. The view should fill the space between the two items and border is used to show that this view does not stretch below keyboard or above navigation bar.
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.
A UIViewController I'm presenting has a UIImageView, namely backgroundImageView, to be the VC's background and set to fill the whole screen.
I want the UIImageView to be presented with a dark blur effect provided by iOS but the following sizing issue temporarily occurs(on device but not on the simulator) when I first present the view controller:
Inside viewDidLoad():
// `locationImage` is passed during the segue
backgroundImageView.image = locationImage
backgroundImageView.contentMode = .scaleAspectFill
let blurEffect = UIBlurEffect(style: .dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = backgroundImageView.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
backgroundImageView.addSubview(blurEffectView)
The above view controller's modalPresentationStyle is set to be as currentContext.
If I temporarily remove the blur effect, I see that the background image I gave the view controller got properly set. However once the blur effect is applied, sizing issues occur momentarily.
What exactly is causing this?
Try to set the frame of blurEffectView inside viewWillLayoutSubviews() or in viewDidLayoutSuviews()
here you can find more information about viewcontroller's lifecycle
Does anyone has problems with the UIBlurEffect on iOS10 ?
For some Reason the background of my button etc just gets a bit transparent and doesn't blurr anymore....
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.ExtraLight)
blurBackgroundView = UIVisualEffectView(effect: blurEffect)
blurBackgroundView.frame = frame
button = UIButton(frame: frame)
blurBackgroundView.layer.masksToBounds = true
backgroundColor = UIColor.clearColor()
addSubview(blurBackgroundView)
addSubview(button)
that's how the code looks....
If I change UIBlurEffectStyle.ExtraLight to UIBlurEffectStyle.Prominent based on the new documentation the Button is just clear... so no color at all!
Add whatever you want not to be blurred to your blurBackgroundView. So instead of:
addSubview(blurBackgroundView)
addSubview(button)
You'll have to:
blurBackgroundView.addSubview(button)
addSubview(blurBackgroundView)
Now every item in the current view, below your blurBackgroundView will get blurred, while your button will stay as it is.
I am trying to create a popup menu over a blurred background. I created the function below to blur the view which works great, but when I try to add the menu, it gets blurred as well. I suppose I could take a snapshot of my view and put that under my popup, but I'm guessing there's a better way...
func blurView() {
let blurEffect = UIBlurEffect(style: .Light)
let effectView = UIVisualEffectView(effect: blurEffect)
effectView.frame = view.frame
view.addSubview(effectView)
effectView.alpha = 0
UIView.animateWithDuration(2.0) { effectView.alpha = 0.8 }
}
If you are just unhiding a previously added menu view to show the menu, then the blurView (which was just added) is probably in front of your menu view as well as everything else. After you add the blurView, when you want to show the menu, try something like:
menuView.hidden = false
view.bringSubviewToFront(menuView)
That should move the menu in front of the effect view so it doesn't get blurred.