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

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.

Related

UINavigationBar Background Color Animation

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.

Customize UINavigationBar with inset image

I have this navigation bar I am trying to implement that looks like this:
I know I can either put the image directly in the nav bar or as a header image below it, but I have no idea how to inset it so that it is contained in both the main container view and breaching the navigation bar as well.
Any ideas would be helpful!
I guess it's achievable using two images:
background: will match the navigation bar frame
icon: let's say an image 50x50 centered to the background in x-axis and y-axis with offset of its height / 2.0
Then you may try to use autolayout constraints doing so:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let navbar = self.navigationController?.navigationBar else {return} // be sure to have defined a navigation controller
navbar.clipsToBounds = false // so the icon will be visible outside the nav bar
let niceBkg = UIImageView(image: UIImage(named: "bkg"))
navbar.addSubview(niceBkg)
niceBkg.translatesAutoresizingMaskIntoConstraints = false
niceBkg.leftAnchor.constraint(equalTo: navbar.leftAnchor).isActive = true
niceBkg.rightAnchor.constraint(equalTo: navbar.rightAnchor).isActive = true
niceBkg.topAnchor.constraint(equalTo: self.navigationController!.view.topAnchor).isActive = true
niceBkg.bottomAnchor.constraint(equalTo: navbar.bottomAnchor).isActive = true
let niceIcon = UIImageView(image: UIImage(named: "icon"))
niceBkg.addSubview(niceIcon)
niceIcon.translatesAutoresizingMaskIntoConstraints = false
niceIcon.widthAnchor.constraint(equalToConstant: 50).isActive = true
niceIcon.heightAnchor.constraint(equalToConstant: 50).isActive = true
niceIcon.centerXAnchor.constraint(lessThanOrEqualTo: niceBkg.centerXAnchor).isActive = true
niceIcon.bottomAnchor.constraint(greaterThanOrEqualTo: niceBkg.bottomAnchor, constant: 25).isActive = true
}
}
on iPhoneX the result is:
About your scenario, you probably need only the icon and a navigationBar with a proper background color. However I added both autolayout configurations because might be useful for someone else, especially if the navigationBar's background is quite complex with border, decorations and so on.

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

Remove border between View and Search Bar

So in Xcode I'm trying to create a seamless search bar. So I'm trying to replicate something like this
Note how the status bar is the same color as the search bar. Now here's the result to my approach.
What I did was add a View to cover up the default status bar with the blue background. Then I added a search bar and changed it's background to blue. For some reason I end up getting a black border between the two, which ruins the "seamless" design. Any ideas on how I can remove the black border in Swift?
For iOS 7+:
searchBar.backgroundImage = UIImage()
Otherwise this will work on all iOS versions:
searchBar.layer.borderWidth = 1
searchBar.layer.borderColor = thatBlueColor.CGColor
Swift 4
searchBar.barTintColor = UIColor.white
searchBar.setBackgroundImage(UIImage.init(), for: UIBarPosition.any, barMetrics: UIBarMetrics.default)
Sample image
Upate Sample code for navigation bar and search bar background color:
Navigation bar color
self.navigationController?.navigationBar.barTintColor = .blue
Search bar color
searchBarProperty.backgroundColor = self.navigationController?.navigationBar.barTintColor
Note : Navigation bar and search bar color must be same.
Sample image with blue navigation bar and blue search bar
In Xcode 8.3 and Swift 3
Create an outlet from your search bar to your ViewController (I called mine searchBarOutlet for this example).
Below viewDidLoad insert the following.
self.searchBarOutlet.backgroundImage = UIImage()
You should have the following:
override func viewDidLoad() {
super.viewDidLoad()
self.searchBarOutlet.backgroundImage = UIImage()
When you run your app the lines will be gone (they will still be visible on storyboard).
In my case, beyond the edge of search bar needed to take the edge off also the navigation bar.
C# code:
NavigationController.NavigationBar.ShadowImage = new UIImage();
NavigationController.NavigationBar.SetBackgroundImage (new UIImage (), UIBarMetrics.Default);
Swift code:
self.navigationController.navigationBar.shadowImage = UIImage()
self.navigationController.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
The best solution to remove top and bottom default borders is:
To set a new empty searchBar background layout in viewDidLoad for example:
searchBar.backgroundImage = UIImage()
I found these answers to be more complicated than they needed to be. You can just modify the constraint that is binding the searchBar view and the other view to -1pts so that it overlaps exactly by the height of the searchBar's margin.
I encountered the same situation when I set the statusBar and searchBar translucent.
In this situation, I couldn't resolve with the answers written here however I could solve by the following approach.
put UIVisualEffectView on self.view (view of your VC)
make custom class of searchBar, which background is transparent
(also let statusBar transparent)
(swift4 code)
class TransparentSearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
makeTransparentBackground()
}
private func makeTransparentBackground() {
for view in self.subviews {
view.backgroundColor = UIColor.clear
for subview in view.subviews {
if let imageview = subview as? UIImageView {
imageview.image = nil
}
}
}
}
}
somewhere in viewDidLoad (statusBar)
let statusWindow = UIApplication.shared.value(forKey:"statusBarWindow") as! UIView
let statusBar = statusWindow.subviews[0] as UIView
statusBar.backgroundColor = UIColor.clear
in Xcode 13
select the search bar and change the search Style to Minimal

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