I want to support a layout similar to Safari, where in horizontally regular environments, buttons are displayed on the navigation bar, but in horizontally compact environments, some buttons are on the navigation bar and some buttons are on the toolbar.
This is what I have in my traitCollectionDidChange
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
let editingContextButton = editing ? resetButton : doneButton
if traitCollection.horizontalSizeClass == .Regular {
navigationController?.toolbarHidden = true
navigationItem.leftBarButtonItems = [editButtonItem(), helpButton]
navigationItem.rightBarButtonItems = [editingContextButton, addButton]
} else if traitCollection.horizontalSizeClass == .Compact{
navigationController?. toolbarHidden = false
navigationItem.leftBarButtonItems = [editButtonItem()]
navigationItem.rightBarButtonItems = [editingContextButton]
navigationController?.toolbarItems = [helpButton, UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil), addButton]
}
}
But it seems there is a problem when transitioning from a regular horizontal size class to a compact one. The items in the navigation bar are always correct, but the toolbar items are cut off or empty.
When bringing up the ViewController, it is always set up and displayed correctly, and it's always right in the horizontally regular size class – there's only a problem when the size class changes from regular to compact.
Here's an example, showing the add button in the bottom right disappearing from the toolbar after the change in size class.
The same thing happens when rotating the device on 5.5" iPhones, and in portrait on iPad, the toolbar is just empty after activating multitasking. What's the problem here? Thanks!
I realized that I was not setting the toolbar items correctly. Instead of
navigationController?.toolbarItems = [helpButton, UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil), addButton]
I should have done:
setToolbarItems([helpButton, UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil), addButton], animated: false)
Related
I have some weird UI bug with inputAccessoryView and keyboard.
Code to add inputAccessoryView in viewDidLoad
let keyboardToolbar = UIToolbar()
keyboardToolbar.items = [
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
UIBarButtonItem(title: "Hide", style: .done, target: self, action: #selector(resignFirstResponder))
]
keyboardToolbar.barStyle = barStyle
keyboardToolbar.sizeToFit()
myTextfield.inputAccessoryView = keyboardToolbar
First time entering screen and tap to textfield works good.
After closing this screen (popViewController) and entering it again and get focus to textField I've got this:
UIToolBar is above keyboard. Empty space have height = keyboard height
ui bug image
this because there is safe area in IOS 11 and this space appear on iphone X
Check this post may be duplicated
iPhone X how to handle View Controller inputAccessoryView?
I do not know the reason but workaround is to call either
view.endEditing
or
textField.resignFirstResponder()
in
viewWillDisappear
My app requires me to add multiple buttons in my navigation bar.
Here is an image of what my nav bar looks like
How do i achieve this type of design?
You can configure your view controller's navigationItem in various ways to achieve this design.
Left side
To allow additional buttons next to the system "back button" on the left:
navigationItem.leftItemsSupplementBackButton = true
This allows you to add a left bar button item for the circular image:
navigationItem.setLeftBarButtonItem(imageItem, animated: trueOrFalse)
imageItem would be a UIBarButtonItem initialized with a customView or image, as discussed in some of the other answers here.
For the back button itself, to achieve a simple "<" without showing the title of the previous view or showing "< Back", you can open the storyboard and set the Back Button text of the previous view controller to a single space, as described in this post.
Title area
For the title area, as discussed in the other answers:
navigationItem.titleView = (a custom view)
Right side
For the right side, you can add multiple buttons:
navigationItem.setRightBarButtonItems([button1, button2, button3, button4], animated: trueOrFalse)
Here, button1, button2, button3, and button4 are each UIBarButtonItems. You would likely initialize these buttons with images.
Looks like you also will want to set the tintColor of the navigation bar to black, so that the bar buttons are rendered in black:
navigationController?.navigationBar.tintColor = .black
All of this code would be done in the view controller, usually in viewDidLoad, unless you need to dynamically change which buttons are shown as the content of your view controller changes.
Try this:
let callImage = UIImage(named: "call")!
let videoImage = UIImage(named: "video")!
let searchImage = UIImage(named: "search")!
let callButton = UIBarButtonItem(image: callImage, style: .plain, target: self, action: #selector(didTapCallButton))
let videoButton = UIBarButtonItem(image: searchImage, style: .plain, target: self, action: #selector(didTapVideoButton))
let searchButton = UIBarButtonItem(image: searchImage, style: .plain, target: self, action: #selector(didTapSearchButton))
navigationItem.rightBarButtonItems = [callButton, videoButton, searchButton]
Selector methods
func didTapCallButton(sender: AnyObject){
...
}
func didTapVideoButton(sender: AnyObject){
...
}
func didTapSearchButton(sender: AnyObject){
...
}
You can create your own view and put down your Images, Labels, etc...
After that, you write:
self.navigationItem.titleView = yourView
UINavigationItem has a property called rightBarButtonItems, this expects an array of UIBarButtonItem which would in your case be each of your buttons.
You can use the initialiser on UIBarButtonItem to specify the image and style:
init(image: UIImage?, style: UIBarButtonItemStyle, target: Any?, action: Selector?)
Initializes a new item using the specified image and other properties.
On your view controller you can access the navigation item through the accessor navigationItem, if you embed your view controller inside a UINavigationController you will need to access the navigationItem on that instance and not the view controller.
You can Add UIView in navigation bar so that you can add multiple buttons in that view.
I am experiencing an issue with the inputAccessoryView after ANY other view has been rotated. For example:
1) go to view A and rotate to landscape
2) then rotate back to portrait.
3) go to view B
in view B I have the below code to assign an inputAccessoryView to a UITextField However, after I rotate any view in the app, the accessory view always attaches to the top of the screen and not the top of the keyboard. If I don't rotate any view the accessory view attaches to the top of the keyboard as expected.
Here is the code used to create and assign the inputAccessoryView, it is called from the viewDidLoad
func addDoneButtonOnKeyboard(){
let doneToolbar = UIToolbar(frame: CGRect(x:0, y:0, width:320, height:50))
doneToolbar.barStyle = UIBarStyle.blackTranslucent
doneToolbar.isTranslucent = true
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace,
target: nil,
action: nil)
let doneButton = UIBarButtonItem(title: "Done",
style: UIBarButtonItemStyle.done,
target: self,
action: #selector(MyVC.doneButtonAction))
doneToolbar.setItems([flexSpace,doneButton], animated: false)
doneToolbar.sizeToFit()
doneToolbar.autoresizingMask=UIViewAutoresizing.flexibleWidth
self.myTextFild.inputAccessoryView = doneToolbar
}
I have searched SO but did not see any other questions/answers similar....
I my case the inputView was a datePicker and I had the same problem. The solution for me was:
textFieldDate.inputView?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
Not sure if this helps for you as well, cause you are using a keyboard as inputView but maybe it helps others with datePicker problems.
I am writing a simple iOS app in XCode 7.3, which I believe puts me using Swift 2.2. I am trying to use a UIDatePicker with UIToolbar with a UITextfield, and for some reason I tapping on the Cancel button seems not to call the method datePickerCancelled on the controller. Everything displays fine(picker, buttons , etc.), but the event won't fire. I have tried several variations of adding the selector to the UIBarButtonItem, and nothing seems to work. As you can see from the code this a pretty trivial case so it escapes why it should be this difficult. Thank you.
override func viewDidLoad() {
super.viewDidLoad()
var datePicker = UIDatePicker()
var datePickerToolbar = UIToolbar()
let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Done, target: nil, action: nil)
let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: #selector(datePickerCancelled))
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
datePickerToolbar.setItems([cancelButton,flexSpace, doneButton], animated: true)
datePicker.userInteractionEnabled = true
cancelButton.enabled = true
self.dateField.inputView = datePicker
self.dateField.inputAccessoryView = datePickerToolbar
}
func datePickerCancelled(){
self.datePicker.resignFirstResponder();
}` I
The problem is this line:
var datePickerToolbar = UIToolbar()
This results in a toolbar of zero size. The Cancel button is visible, but it is outside of its superview, namely the toolbar — because the toolbar has zero size.
A view outside of its superview cannot be tapped.
You can easily confirm this by setting the toolbar's clipsToBounds to true and running the app. The Cancel button will now be invisible, because things outside the toolbar are no longer shown.
If the event did fire, your datePickerCancelled function still wouldn't do anything:
func datePickerCancelled(){
self.datePicker.resignFirstResponder();
}
Your date picker was never first responder, so that line wouldn't cause anything to happen.
Perhaps you meant this:
func datePickerCancelled(){
self.dateField.resignFirstResponder();
}
The first screenshot is taken before playing video in full screen.
The second is taken after the video is opened in full screen and closed.
Any idea why navigation toolbar has extend?
Note: The hamburger button is not the part of the navigation item. It is faked in overlay in parent that holds its child controller inside standard container.
Nothing special inside the source:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
bbiListic = UIBarButtonItem(image: UIImage(identifier: .IcoHeaderListic), style: .Plain, target: self, action: #selector(UIViewController.showListic))
bbiFavorite = UIBarButtonItem(image: UIImage(identifier: .IcoHeaderStarEmpty), style: .Plain, target: self, action: #selector(LiveDogadjajViewController.toggleFavorite(_:)))
...
let items = [bbiListic!,bbiFavorite!]
navigationItem.rightBarButtonItems = items
}
func someRefresh() {
var items = [UIBarButtonItem]()
items.append(bbiListic!)
...
navigationItem.rightBarButtonItems = items
}
Update:
This appears to be a problem only on the latest version of iOS, 9.3
From your screenshots, it look like that height of status bar gets doubled. Try this:-
Before playing your video, hide the status bar
UIApplication.sharedApplication().setStatusBarHidden(true, withAnimation: .None)
After ending the video, show the status bar
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: .None)
Pre-requisite:
a) Uncheck "extends edges" by selecting your uiviewcontroller from main.storyboard
b) no constraints exist between your video player and container controller
Solution:
Check if any of your buttons on the nav bar are bottom constrained. Either remove that constraint or apply a fixed height constraint to your custom nav bar view so that it stays the same height.
I contacted the Apple developer technical support about this issue.
They have determined it is probably a bug in iOS 9.3.
Bug id: 26439832, iOS SDK
This is easy workaround for view controller on stack:
// ... add this to init method
let nc = NSNotificationCenter.defaultCenter()
nc.addObserver(self, selector: #selector(didExitFullscreen(_:)), name: MPMoviePlayerDidExitFullscreenNotification, object: nil)
func didExitFullscreen(notification: NSNotification)
{
// hack, fix for 9.3.
if #available(iOS 9.3, *)
{
navigationController?.setNavigationBarHidden(true, animated: false)
navigationController?.setNavigationBarHidden(false, animated: false)
}
}