I have a UITableViewController and when the user taps an "edit" button, I display a toolbar at the bottom of the screen. I create a shadow on top of the toolbar. It looks great (until I changed the color to make it more obvious shat's going wrong). When I scroll the table up, as soon as the bottom of the table comes out from behind the toolbar, the toolbar colors all become transparent or do something that looks transparent.
Here is a picture of the screen when the table has data that flows under the toolbar:
Here is a picture of the screen as I drag the table up and the last item is only partly under the toolbar:
Finally, when I drag the table as fr up as I can and the last item is completely above the toolbar, I get this:
It is obvious that the toolbar background is changing opacity as the table is scrolling. I need to disable this. I have tried various combinations of checking and unchecking the "under bottom bars" and "under opaque bars" in the storyboard. I have tried adding an empty footer and not adding an empty footer to the table. Here is the code that creates the toolbar. Note that I'm using a table view controller within a tab bar controller and hiding the tab bar when the user edits the table:
public func setEditingWithTabController( _ editing: Bool, animated: Bool, tabController: UITabBarController ) {
if navigationController == nil || navigationController!.toolbar == nil {
return
}
navigationController!.setToolbarHidden( !editing, animated: true )
var items = [UIBarButtonItem]()
let flexible = UIBarButtonItem( barButtonSystemItem: .flexibleSpace, target: self, action: nil )
deleteButton = UIBarButtonItem(title: Strings.Delete.localized, style: .plain, target: self, action: #selector(didPressDelete))
deleteAllButton = UIBarButtonItem(title: "Delete All", style: .plain, target: self, action: #selector(didPressDeleteAll))
items.append( deleteAllButton! )
items.append( flexible )
items.append( deleteButton! )
tabController.setToolbarItems( items, animated: false ) // toolbar gets its items form the current view controller
let bar = navigationController!.toolbar!
bar.backgroundColor = AppSettings.appBackgroundColor.isDark ? AppSettings.appBackgroundColor.lighten( amount: 0.05 ) : AppSettings.appBackgroundColor.darken( amount: 0.05 )
bar.barTintColor = AppSettings.appBackgroundColor.isDark ? AppSettings.appBackgroundColor.lighten( amount: 0.05 ) : AppSettings.appBackgroundColor.darken( amount: 0.05 )
bar.layer.shadowColor = UIColor.red.cgColor //AppSettings.appBackgroundColor.isDark ? UIColor.lightGray.cgColor : UIColor.lightGray.cgColor
bar.layer.shadowOpacity = AppSettings.appBackgroundColor.isDark ? 0 : 0.5
bar.layer.shadowOffset = CGSize.zero
bar.layer.shadowRadius = AppSettings.appBackgroundColor.isDark ? 0 : 20
bar.layer.borderColor = UIColor.clear.cgColor
bar.layer.borderWidth = 0
bar.clipsToBounds = false
bar.isTranslucent = false
bar.isOpaque = true
tableView.setEditing( editing, animated: true )
if editing {
refreshControl?.removeFromSuperview()
tableView.backgroundView = nil
} else {
refreshControl = UIRefreshControl()
refreshControl!.addTarget( self, action: #selector( refreshData(_:) ), for: .valueChanged )
tableView.backgroundView = refreshControl
}
}
This same thing happened before I added any of the shadow layer code but was much less obvious with the background simply changing from white to light gray when I scrolled the table.
The function above gets called when the user taps the "edit" button in the app header. Obviously I don't need to configure the toolbar every time this happens and especially when hiding it - I'll worry about fixing all of that later. I need to figure out why I can't get a clean unchanging toolbar. heck, I can't even seem to get rid of the gray line at the top of the toolbar even though I'm setting the border size to zero. This thing has a mind of its own!
[CORRECTION]...
I was not setting a background color. Now when I set a background color, I get the translucency only under the toolbar in the safe area inset like this:
In iOS 15, UIKit has extended the usage of the scrollEdgeAppearance, which by default produces a transparent background, to all navigation bars. The background is controlled by when your scroll view scrolls content behind the navigation bar. Your screenshots indicate that you are scrolled to the top, and so the navigation bar has selected its scrollEdgeAppearance over the standardAppearance that it would use when scrolled, and on previous versions of iOS.
To restore the old look, you must adopt the new UINavigationBar appearance APIs, UINavigationBarAppearance. Remove your existing customizations and do something like this:
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = <your tint color>
navigationBar.standardAppearance = appearance;
navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance
In the general case, it is the last line navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance, which resolves the issue by having the UINavigationBar use the same appearance for both its standard and edge states. Also note that this will cause the scroll view to underlap the navigation bar – we recommend against setting UINavigationBar.isTranslucent = true.
You can also use the appearance proxy with the code above, but substituting navigationBar.appearance().scrollEdgeAppearance = appearance for the last line (as you are constructing your own appearance object – the idea is to just make sure both scrollEdge and standard appearances are the same).
Credit: https://developer.apple.com/forums/thread/682420
Same can be achieved for Tabbar
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = <your tint color>
self.tabBarController?.tabBar.standardAppearance = appearance;
self.tabBarController?.tabBar.scrollEdgeAppearance = self.tabBarController?.tabBar.standardAppearance
Related
I already had a UISearchBar (search icon is bookmarkButton inside searchTextField) like that:
Search Bar
searchBar code
private func setupSearchBar() {
searchBar.translatesAutoresizingMaskIntoConstraints = false
searchBar.backgroundImage = UIImage()
searchBar.setImage(UIImage(), for: .search, state: .normal)
searchBar.showsBookmarkButton = true
searchBar.setImage(UIImage(systemName: "magnifyingglass")?.withTintColor(self.darklightcolor, renderingMode: .alwaysOriginal).applyingSymbolConfiguration(.init(pointSize: 22)), for: .bookmark, state: .normal)
cancelButtonSearchBar = UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self])
cancelButtonSearchBar.tintColor = .systemPink
searchBar.layer.borderColor = darklightcolor.cgColor
searchBar.layer.borderWidth = 1.5
searchBar.layer.cornerRadius = 19
searchBar.layer.backgroundColor = UIColor.clear.cgColor
searchBar.searchTextField.backgroundColor = .clear
searchBar.delegate = self
view.addSubview(searchBar)
}
Now I want to add a gray line to searchBar next to search icon like this
But I cant find anyway to add that line. Can anyone help me?
That separator is not part of UISearchbar. You should not play with the internal view hierarchy of the searchbar since it may change and it is going to break your implementation. There is also something in your code that raises a flag, setting the appearance() for buttons when contained in UISearchbars may lead to unintended tinting of buttons in other searchbars within the app. As a practice if you want to set it for all, I would suggest applying those appearance modifications in a single place, otherwise you will end up looking everywhere in your code for the one place where you set the value.
The best approach if that design is so important would be to implement something custom, at that point, it would be up to you to make the hierarchy and you could modify the hierarchy because it was created by you.
That being said, if you still want to continue with this approach, you may add this extension:
extension UISearchBar {
var cancelButtonView: UIView? {
self.searchTextField
.superview?
.subviews
.first(where: { $0.description.contains("Button") })
}
}
And use it when setting constraints for the view you've added.
iOS 13 has a whole new set of classes for configuring navigation bars and bar button items:
UIBarAppearance
UINavigationBarAppearance
UIBarButtonItemAppearance
UIBarButtonItemStateAppearance
How do I use these to give bar button items a global appearance? Let's say I want all bar button items to have a common background image. I used to say:
UIBarButtonItem.appearance().setBackgroundImage(im, for:.normal, barMetrics:.default)
This seems to be just what is superseded by these new classes. So what's the right way now?
It's verbose, but in some ways it's clearer. You can configure a navigation bar appearance and assign it to the navigation bar's standardAppearance through the proxy:
let app = UIBarButtonItemAppearance()
app.normal.backgroundImage = im
let navbarapp = UINavigationBarAppearance()
navbarapp.configureWithOpaqueBackground()
navbarapp.buttonAppearance = app
UINavigationBar.appearance().standardAppearance = navbarapp
The only problem with that is that it assigns the same background image to back button items. So if that's not desired, you have to code defensively, assigning back button items an empty (not nil) image:
let app = UIBarButtonItemAppearance()
app.normal.backgroundImage = im
let navbarapp = UINavigationBarAppearance()
navbarapp.configureWithOpaqueBackground()
navbarapp.buttonAppearance = app
let back = UIBarButtonItemAppearance()
back.normal.backgroundImage = UIImage() // prevent back button item
navbarapp.backButtonAppearance = back
UINavigationBar.appearance().standardAppearance = navbarapp
As titled, I'm using a UIButton as the titleView in the navbar. Is there a way for me to make the UIButton larger along with the navbar? When I set self.navigationController?.navigationBar.prefersLargeTitles = true, what I got is this,
I can't find any documentation on this, so I played a bit. It seems in iOS 11, you won't be able to get that title to be a button and display large at the left.
Also, I ttied playing with the frame size of the button (added below). I was unable to increase the button size, no matter what I set the frame to.
To recreate, I set up a Single View project. I embedded the view controller in a navigation controller.
In ViewController.swift's viewDidLoad, I added this code:
let titleButton = UIButton(type: .roundedRect)
titleButton.setTitle("Hello Button!", for: UIControlState.normal)
let navController = parent as! UINavigationController
navController.navigationBar.topItem!.title = "Hello???"
navController.navigationBar.topItem!.titleView = titleButton
navController.navigationBar.prefersLargeTitles = true
This ends up looking something like your example.
IF I:
set .title to empty string, or remark the line out: navbar is stretched, and no title text shows (or title text set in Interface Builder shows)
remark out .prefersLargeTitles, or set it to false: the navbar is the normal height, the button displays, but no title text displays.
remark out the titleView line, AND:
leave the .prefersLargeTitles set to true: the title text displays large at the left, and the navbar's height is stretched.
set the .prefersLargeTitles to false: the title text displays in the top center, and the navbar is normal height.
i'm attempting to add a done button to my nav bar (built directly UINavigationBar not with a controller). The done button appears fine, the action works, but it has no padding from the edge of the nav bar.
i tried adding a second bar button item with fixed space but it has no effect.
what's even weirder to me is that when i tried adding the button twice [rightButton,rightButton]
it made space for 2 buttons but only the first one showed up the second one didn't actually appear.
thanks for you help i've attached some code and photos for reference.
let rightButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Done, target: self, action: "doneAction:")
let rightButtonPadding = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
rightButtonPadding.width = 50
let items = UINavigationItem()
items.title = name.uppercaseString
items.rightBarButtonItems = [rightButton,rightButtonPadding]
items.hidesBackButton = true
navBar.pushNavigationItem(items, animated: false)
change the order to [rightButtonPadding ,rightButton]
From Documentation:
"Items are displayed right-to-left in the same order as they appear in
the array. Thus, the first item in the array is the rightmost item and
other items are added to the left of the previous item."
Try to play with UIBarButtonItemStyleDone/UIBarButtonItemStylePlain. Also please check this thread: iOS7 excessive navigationbar button padding
I'm working on an app and I'm not sure how to change the color of the bottom toolbar in swift. I would like to have it as a custom image to match my navigation bar. Does anyone have a code for that or just to change the color. Thank you
Change background color:
self.toolbar.barTintColor = UIColor.redColor()
Change background image:
self.toolbar.setBackgroundImage(UIImage(named: "BackgroundImage"), forToolbarPosition: .Bottom, barMetrics: .Default)
NOTE: All my answers are in Swift 3
In order to change the background color of the toolbar do the following:
self.toolbar.isTranslucent = false
self.toolbar.barTintColor = UIColor.red
This code is similar to #Bannings answer, however his answer is missing the isTranslucent property, which must be set to false first. Otherwise, it won't work.
In order to change the background image do the same as #Bannings suggested:
self.toolbar.setBackgroundImage(UIImage(named: "BackgroundImage"), forToolbarPosition: .bottom, barMetrics: .default)
It should be stated here that the background image is visible due the fact that the isTranslucent property is set to true by default (assuming the background image is not opaque).
It always helps to read the Apple's description on the isTranslucent property for toolbars:
A Boolean value that indicates whether the toolbar is translucent (true) or not (false).
The default value is true. If the toolbar has a custom background image, the default is true if any pixel of the image has an alpha value of less than 1.0, and false otherwise.
If you set this property to true on a toolbar with an opaque custom background image, the toolbar will apply a system opacity less than 1.0 to the image.
If you set this property to false on a toolbar with a translucent custom background image, the toolbar provides an opaque background for the image using black if the toolbar has black style, white if the toolbar has default, or the toolbar’s barTintColor if a custom value is defined.
If the tool bar is anchored with navigation controller, go to IB to change the color.
1 . go to your navigation controller,
2 . show "document outline"
3 . select "Toolbar" under the navigation controller (usually, it is below Navigation bar)
one the right side, choose your prefered "Bar Tint" / "Translucent"
This also work on swift 3
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.barTintColor = UIColor.red
Your UIBarStyle should be default.
This also works on swift 3:
UINavigationBar.appearance().barTintColor = UIColor.red