UINavigationBar Background Color Animation - ios

I am trying to implement Apple's detail screen from their Podcast, Music, and App Store Apps. The navigation bar starts off transparent then increases the alpha the further down you scroll.
I have the desired effect working changing the alpha value of the navigationBar itself. However, this also affects the buttons within the navigationBar.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var offset = scrollView.contentOffset.y / 350
if offset > 1 {
offset = 1
self.navigationController?.navigationBar.alpha = offset
self.navigationItem.setHidesBackButton(false, animated: false)
} else {
self.navigationController?.navigationBar.alpha = offset
self.navigationItem.setHidesBackButton(true, animated: false)
}
}
I believe I need to change either
The Navigation Bar's backgroundColor
The Navigation Bar's barTintColor
Upon implimenting each of these and replacing:
self.navigationController?.navigationBar.alpha = offset
with
self.navigationController?.navigationBar.barTintColor = Color.background?.withAlphaComponent(offset)
or
self.navigationController?.navigationBar.backgroundColor = Color.background?.withAlphaComponent(offset)
The animation does not transition, it simply goes from transparent to opaque.
I am setting up my Navigation Bar on this controller using iOS 13 methods
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
self.navigationController?.navigationBar.standardAppearance = appearance
Any help with this much appreciated.

Related

How to create App Store Style Navigation Bar

I'm trying to recreate the App Store navigation bar animation. What I'm currently doing is adjusting the alpha of the navigation bar based on the scroll position.
The issue is that when you slide back the navigation bar does not appear in the previous view so I set the alpha back to one in viewWillDissapear (which doesn't work the best as seen in the gif).
With regards to the back button, if you change the alpha of the navigation bar the back button is also affected. Apple has a different back button (the circle with the arrow) when the navigation bar is not visible and this button disappears with an animation as the navigation bar appears and the default back button appears. I'm not sure if they're just overlaying a new one.
This question has been asked before but the accepted answer was to make the navigation bar transparent then change it's opacity as you scroll. This doesn't create the same effect as the default navigation bar isn't white but translucent and you also lose the border line at the bottom.
Swift 5
override func viewWillDisappear(_ animated: Bool) {
navigationController?.navigationBar.alpha = 1
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let currentVerticalOffset = scrollView.contentOffset.y
var percentageVerticalOffset = CGFloat() //currentVerticalOffset / maximumVerticalOffset
if currentVerticalOffset > collectionView.frame.size.height {
percentageVerticalOffset = currentVerticalOffset / currentVerticalOffset
self.title = "The Barn Owl"
//self.navigationItem.setHidesBackButton(false, animated: true)
} else {
percentageVerticalOffset = currentVerticalOffset / collectionView.frame.size.height
//self.navigationItem.setHidesBackButton(true, animated: true)
self.title = ""
}
// let color = UIColor.init(red: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: percentageVerticalOffset)
// navigationController?.navigationBar.backgroundColor = color
navigationController?.navigationBar.alpha = percentageVerticalOffset
}

How to prevent uiSearchBar to become translucent in the Hidding animation?

I'm testing a simple animation to hide a uiSearchBar with a button tap.
var searchBarActive = false
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Search", style: .done, target: self, action: #selector(searchTapped))
}
#objc func searchTapped(sender: UIBarButtonItem) {
if searchBarActive {
searchBarActive = false
UIView.animate(withDuration: 2) {
self.searchBar.isHidden = true
// self.searchBar.searchBarStyle = .default
// self.searchBar.barStyle = .default
// self.searchBar.tintColor = .none
// self.searchBar.barTintColor = .none
// self.searchBar.tintAdjustmentMode = .normal
// self.searchBar.isTranslucent = false
}
} else {
searchBarActive = true
UIView.animate(withDuration: 2) {
self.searchBar.isHidden = false
}
}
}
The animation works fine, but I noticed when isHidden is set to true, the searchBar seems to turn translucent, because the color it gets is the color of the view that is behind it.
I noticed that because I was using a random color for that view, if you use white for example, you wouldn't even notice.
I undertand that it would appear to be changing its color if the isHidden property would fade the bar, but by setting the animation with a high duration it looks like it comes from the top to the bottom (like a sliding motion) when isHidden is set to false and from bottom to top when isHidden is set to true.
I tried the commented code (both inside and outside the animation closure) expecting it would fix it, but it looks like something is still overriding these values. I don't have anything else that would cause this problem in the project.
I checked this question as well: Setting translucent to NO on UISearchBar and then tried the isTranslucent one, but still didn't work.
Is this expected or should I be doing something different?
Thank you in advance!
As I have not find an answer to the question, I did resolve it by making the background color that is in the view behind this search bar white.
I have this search bar inside a stack view, so I just changed the stack view background color to white.
It fixes the problem but still I wonder why that happens.

iOS 13 remove UIView for simulate status bar background

I'm working with a UITableViewController which when scrolling makes the navigationBar disappear. Now when the navigation bar is hidden when the user swipes the table view the contents of the cells are seen below the status bar ...
To solve this problem I tried to insert a UIView to simulate a background of the status bar and everything works but the problem is that when I close the UITableViewController the background view of the status bar is not removed from the superview
For now my code is this, can you help me understand where I am wrong? why can't I remove the UIView from the superview?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupStatusBarView()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.isHidden = true
UIApplication.shared.windows.first?.viewWithTag(1)?.removeFromSuperview()
}
//MARK: - Setup Status Bar View
func setupStatusBarView() {
let height = view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
let statusBarView = UIView()
statusBarView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height:height+5)
statusBarView.backgroundColor = .systemBackground
statusBarView.tag = 1
UIApplication.shared.windows.first?.addSubview(statusBarView)
}
viewDidLayoutSubviews get calls multiple times and you have put setupStatusBarView() in viewDidLayoutSubviews that means your background view has been added multiple times and this is totally wrong flow!
You are removing topmost view only not previous ones!
You should set frame in viewDidLayoutSubviews and should add the view from viewDidLoad!
try this one
let subviewArray = UIApplication.shared.windows.first?.subviews
for view in subviewArray!{
if view.tag == 1{
view.removeFromSuperview()
}
}

Black background on transparent UITabBar

I am trying to make a blurred background the UITabBar for my UITabViewController, and the idea is to have it be blurred and transparent so that the views underneath can be seen scrolling by.
Unfortunately I cannot for the life of me get the tab bar to be transparent. No matter what I do, there is always some black background to the tab bar that prevents the underlying view controllers from showing through.
If I change the alpha of the UITabBar to something low I can see that the tableview is indeed behind it, however you can see that the UITabBar has some sort of background to it that is preventing the tableview from fully showing through (and I don't want to bar button items to be invisible, just the tab bar background).
How can this be?
In the custom tab bar's view did load I have:
self.tabBar.translucent = true
self.tabBar.alpha = 0.3
self.tabBar.backgroundColor = UIColor.clearColor().colorWithAlphaComponent(0.0)
self.tabBar.layer.backgroundColor = UIColor.clearColor().colorWithAlphaComponent(0.0).CGColor
self.tabBar.backgroundImage = nil
self.tabBar.shadowImage = nil
and in the AppDelegate I have:
UITabBar.appearance().barTintColor = UIColor.clearColor()
UITabBar.appearance().tintColor = kColorAccent
UITabBar.appearance().translucent = true
UITabBar.appearance().translucent = true
UITabBar.appearance().backgroundColor = UIColor.clearColor()
UITabBar.appearance().backgroundImage = nil
UITabBar.appearance().layer.backgroundColor = UIColor.clearColor().CGColor
UITabBar.appearance().shadowImage = nil
...yeah It's excessive but I want to try everything.
Any ideas on what to do?
Make a UITabBar transparent
Assign a clear image to its backgroundImage. You can use a 1x1 clear.png, or create one programmatically:
self.backgroundImage = UIImage.imageWithColor(UIColor.clearColor())
This will make the UITabBar transparent:
Add a blur effect
Insert a UIVisualEffectView as the rearmost subview.
let frost = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
frost.frame = self.bounds
self.insertSubview(frost, atIndex: 0)
This will insert a UIBlurEffect (frost):
Example
Set the Custom Class for the UITabBar of the Tab Bar Controller to FrostyTabBar.
You have a few options to supply a clearColor image. You can create a clear.png image with an alpha of 0. A programmatic elegant solution is described here.
If using a clear.png, assign it to the Background Image in the Attribute Inspector:
In Interface Builder, pick Style: Default & Translucent.
Once you take control of the background blur with a UIVisualEffectView, you can in turn supply any UIVisualEffect you so desire.
The entire FrostyTabBar class looks like this:
import UIKit
class FrostyTabBar: UITabBar {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let frost = UIVisualEffectView(effect: UIBlurEffect(style: .light))
frost.frame = bounds
frost.autoresizingMask = .flexibleWidth
insertSubview(frost, at: 0)
}
}
► Find this solution on GitHub and additional details including a 1x1 clear.png on Swift Recipes.
I found a prefect solution, you only need to subclass UITabBar and then do the following actions to clean that annoying views
class MainTabBar: UITabBar {
var cleanDone = false
override func layoutSubviews() {
super.layoutSubviews()
self.deleteUnusedViews()
}
func deleteUnusedViews() {
if !self.cleanDone {
var removeCount = 0
for (_, eachView) in (self.subviews.enumerate()) {
if NSStringFromClass(eachView.classForCoder).rangeOfString("_UITabBarBackgroundView") != nil {
eachView.removeFromSuperview()
removeCount += 1
}
if NSStringFromClass(eachView.classForCoder).rangeOfString("UIImageView") != nil {
eachView.removeFromSuperview()
removeCount += 1
}
if removeCount == 2 {
self.cleanDone = true
break
}
}
}
}
}
the only solution that worked for me was this:
UITabBar.appearance().shadowImage = UIImage()
UITabBar.appearance().backgroundImage = UIImage()
and set: (you can do this in storyboard as well)
UITabBar.appearance().barTintColor = UIColor.clear
but what i have to set in storyboard is:
tabbar : translucent -> true

TableView Showing Behind Tab Bar

I am updating my app to use iOS 7 and I'm having a problem with a table view. My tab bar is translucent. The problem is when I scroll to the bottom of my table view, part of the last cell is still behind the tab bar. I'd like to have a bit of space between the last cell and the tab bar. I could fix this by using an opaque tab bar instead, but I want to keep it translucent.
Try setting
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.automaticallyAdjustsScrollViewInsets = NO;
Inside the tableview controller
Swift 4.x
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(0, 0, self.tabBarController!.tabBar.frame.height, 0)
self.yourTableView.contentInset = adjustForTabbarInsets
self.yourTableView.scrollIndicatorInsets = adjustForTabbarInsets
Check the screen shot
Check the under top Bar and Un-checke under Bottom Bar
SWIFT 3
put this inside viewDidLoad of your tableViewController:
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
self.automaticallyAdjustsScrollViewInsets = false
Swift 3.0
This is what worked for me. In your Custom ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(self.tabBarController!.tabBar.frame.height, 0, 0, 0);
//Where tableview is the IBOutlet for your storyboard tableview.
self.tableView.contentInset = adjustForTabbarInsets;
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets;
}
Not to sure I like the solution but it works for me.
With iOS 11 I have no issue, I simply use the following in viewDidLoad():
self.collectionView.bottomAnchor.constraint(self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
However on iOS 10 I need to hack my way like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let tabBarHeight: CGFloat = (self.parent?.tabBarController?.tabBar.frame.size.height)!
if #available(iOS 11.0, *) {
} else {
self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -tabBarHeight).isActive = true
}
}
This is working for me
override func viewDidLoad() {
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
}
If any view shows behind a UITabBar you can grab the bottomLayoutGuide and make adjustments at runtime. What I do is have a BaseViewController that all my view controllers inherit from. Then if the tab bar is visible we adjust the view like so:
import UIKit
class BaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
//Ensures that views are not underneath the tab bar
if tabBarController?.tabBar.hidden == false {
var viewBounds = self.view.bounds;
var bottomBarOffset = self.bottomLayoutGuide.length;
self.view.frame = CGRectMake(0, 0, viewBounds.width, viewBounds.height - bottomBarOffset)
}
}
}
Since I don't use storyboards (where you can click a checkbox in IB to fix this problem), this has been the best solution I have found.
It is really hard to resolve the issue without detail information or actual codes. I have similar issue of tabview behind UItabBar in my project. The solutions offered here do not work in my case. After exploring my codes, I found a solution for my case.
Here is brief explanation of my case. I have a UItabBar in main view with two tab buttons. In one tab view, there is table view. If user taps on a row, a detail view is presented by using navigation controller. In the detail view, the tab bar is hidden, and a toolbar is showing at the bottom.
In order to bring tab bar back and hide the toolbar when the main view is brought back, I have to explicitly show tab bar and hide toolbar in the event of viewWillAppear:
class myMainViewController: UITableViewController {
private var tabBarHidden: Bool? = {
didSet {
self.tabBarController?.tabBar.isHidden = tabBarIsHidden ?? true
}
}
private var toolBarIsHidden: Bool? {
didSet {
let hidden = toolBarIsHidden ?? true
self.navigationController?.toolbar.isHidden = hidden
self.navigationController?.setToolbarHidden(hidden, animated: true)
}
}
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarIsHidden = false
self.toolBarIsHidden = true
}
...
}
I finally realize that the visibility of bar at the bottom is set in the event of viewWillAppear. At that time, the tableView or scroll view's content insets are set already based on no bar at the bottom. That's why my tableView is behind the bottom bar.
The solution I found is to reset content insets in the event of viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
// In the event of viewWillAppear, visibilities of tool bar and tab bar are set or changed,
// The following codes resets scroll view's content insets for tableview
let topInset = self.navigationController!.navigationBar.frame.origin.y +
self.navigationController!.navigationBar.frame.height
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(
topInset, 0,
self.tabBarController!.tabBar.frame.height, 0)
self.tableView.contentInset = adjustForTabbarInsets
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets
}
The best approch would be to Embed TabBarController to your ViewController (Editor -> Embed In -> TabBar Controller)and set the bottom of the tableview to be bottom of safe area of viewcontroller. The other ways wont be as perfect as this one.
You need to adjust the height of the table view. Just leave 49px at the bottom, as the tabbar height is 49 px. Adjust the height of table view so that it leaves 49px space below it.

Resources