How to Resize UIToolBar from a UIDatePicker? - swift

I'm trying to show a UIDataPicker when the user clicks on the UITextField. It works. However, the size of it seems ridiculous.
Please, check this image from the simulator
It gives a warning regarding the constraints I've used in the storyboard:
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000036017c0 'TB_Trailing_Trailing' H:[_UIModernBarButton:0x7fa478418640'Cancel']-(0)-| (active, names: '|':_UIButtonBarButton:0x7fa478418470 )>
func showDatePicker() {
// Format date
bdPicker.datePickerMode = .date
//ToolBar
let toolbar = UIToolbar();
toolbar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(donedatePicker));
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelDatePicker));
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
BDTextField.inputAccessoryView = toolbar
BDTextField.inputView = bdPicker
}
I tried to modify: toolbar.sizeThatFits() and I gave random values, but the app crashed. So, I'm looking for a strategy that I can make this UIDatePicker fits half of the page at least.

Related

How to set textField becomeFirstResponder via selector

I wrote a UITextfield extension to build my own toolbar for custom inputs:
func addToolbar(selector: Selector? = nil) {
let action = selector == nil ? #selector(resignFirstResponder) : selector
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: action)
let toolbar = UIToolbar()
toolbar.items = [flexibleSpace, doneButton]
toolbar.sizeToFit()
toolbar.layoutIfNeeded()
inputAccessoryView = toolbar
returnKeyType = .done
}
I use it like this:
let myPickerView = UIPickerView()
myPickerView.dataSource = myPickerViewBehavior
myPickerView.delegate = myPickerViewBehavior
if let selected = value {
let selectedIndex = PickableItemType.index(of: selected.rawValue)
myPickerView.selectRow(selectedIndex, inComponent: 0, animated: true)
}
myTextField.delegate = self
myTextField.inputView = myPickerView
myTextField.addToolbar(selector: #selector(nextTextField.becomeFirstResponder))
As you can imagine I want the next UITextField to becomeFirstResponder after the user pressed done in the toolbar.
But nothing happening.
For testing I've tried using a simple #objs func with print statement and/or nextTextField.becomeFirstResponder inside, same result.
I've also tried to use UITapGestureRecognizer with #selector(nextTextField.becomeFirstResponder), also same result.
So I guess it has something to do with the #selector mechanic why the code doesn't work.
P.S.: if selector == nil inside addToolbar , #selector(resignFirstResponder) works fine.
I believe the problem is caused by the wrong target: while setting up doneButton.
If you also supply the target in addToolbar: method like:
func addToolbar(target: Any? = nil, selector: Selector? = nil) {
let object = target ?? self
let action = selector ?? #selector(resignFirstResponder)
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: object, action: action)
let toolbar = UIToolbar()
toolbar.items = [flexibleSpace, doneButton]
toolbar.sizeToFit()
toolbar.layoutIfNeeded()
inputAccessoryView = toolbar
returnKeyType = .done
}
This should work:
firstTextField.addToolbar(target: secondTextField, selector: #selector(becomeFirstResponder))
Here's a small test result:
https://imgur.com/R7R1ee9

Get returnKeyType text for UITextField

I am creating a custom UIToolBar to add as inputAccessoryView to a UITextField, I would like to add a UIBarButtonItem on the right side of this toolbar to serve as the return key of this textfield, and the text of this barButtonItem should be the same as the keyboard of that textfield.
My approach was:
let buttonDone = UIBarButtonItem(title: myTextField.returnKeyType, style: .done, target: self, action: #selector(pickerDone)
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
// toolbar
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.items = [space, buttonDone]
toolBar.sizeToFit()
// setup input
myTextField.inputAccessoryView = toolBar
But with this i get this error:
Cannot convert value of type 'UIReturnKeyType?' to expected argument type 'String?'
So, I try something like:
title: myTextField.returnKeyType.text
But returnKeyType doesn't has a .text variable or similar...
Is there any way of doing this?
Should I go another way?
There is no built-in way to convert the UIReturnKeyType enum to a string. You will need to write your own code using a switch on the all the possible values.
Here's one solution using an extension. Add support for the other values as needed.
extension UIReturnKeyType {
var label: String {
switch self {
case .default:
return "Return"
case .go:
return "Go"
case .done:
return "Done"
default:
return "Enter"
}
}
}
Then you can use this as:
let buttonDone = UIBarButtonItem(title: myTextField.returnKeyType.label, style: .done, target: self, action: #selector(pickerDone)

UIDatePicker: select current date (swift)

I have a UIDatePicker in which some date is set by initial. With addTarget, I listen to thevalueChanged events and everything works correctly when the wheel is spinning.
I want to allow users to directly select the initial date (for example, by clicking on it). But now the event comes only when the wheel is spinning. To put the current date, the user will have to:
Put a new date
Go to the previous date
My code:
class Picker {
init() {
let datePicker = UIDatePicker()
datePicker.setDate(Date(), animated: false) // Set initial date
datePicker.addTarget(self, action: #selector(handleDatePicker(sender:)), for: .valueChanged)
}
#objc func handleDatePicker(sender: UIDatePicker) {
print(sender.date)
}
}
Picker()
Screenshot:
SOLUTION:
The most correct solution turned out to be the addition the UIToolBar with Done button. The code can be found here: example with UIToolBar
to select first index value on click done button in datePicker
func pickUp(_ textField : UITextField){
// UIPickerView
self.myPickerView = UIPickerView(frame:CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 216))
self.myPickerView.delegate = self
self.myPickerView.dataSource = self
self.myPickerView.backgroundColor = UIColor.white
textField.inputView = self.myPickerView
// ToolBar
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.tintColor = UIColor.darkGray
toolBar.sizeToFit()
// Adding Button ToolBar
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(UrVC.doneClick))
let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(UrVC.cancelClick))
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
textField.inputAccessoryView = toolBar
}
#objc func doneClick() {
pickerTxtfld.resignFirstResponder()
let indexPath = myPickerView.selectedRow(inComponent: 0)
pickerTxtfld.text = dataArray[indexPath]
}
#objc func cancelClick() {
pickerTxtfld.resignFirstResponder()
}

xcode swift nulpad with next button not working

I am relatively new to coding so please bear with me. I have written a simple app which has 6 text fields that need to completed before an action button is pressed to perform a mathematical calculation.
I have everything done but i can't get my next button to move to the next text field. I have added the tool bar and the done and next button but when i press the next button the app crashes and i get a "Thread 1: signal SIGABRT".
I have been through the internet and I am struggling to see how do do it. I would have thought this would be pretty straight forward.
I just can't make the next button perform the action to the next box.
Here is my code
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
//IBOutlet UITextFields are here
override func viewDidLoad() {
super.viewDidLoad()
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.sizeToFit()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let fixedSpaceButton = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(self.doneClicked))
let nextButton = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(self.textFieldShouldReturn(_:)))
toolBar.setItems([fixedSpaceButton, nextButton, flexibleSpace, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
box1.delegate = self
box2.delegate = self
box3.delegate = self
box4.delegate = self
box5.delegate = self
box6.delegate = self
box1.becomeFirstResponder()
}
func doneClicked() {
view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
switch textField
{
case box1: box2.becomeFirstResponder()
break
case box2: box3.becomeFirstResponder()
break
case box3: box4.becomeFirstResponder()
break
case box4: box5.becomeFirstResponder()
break
case box5: box6.becomeFirstResponder()
break
default: textField.resignFirstResponder()
}
return true
}
//IBAction for maths calculations are here
I appreciate any help in advance. I literally have no hair left.
Thanks

Code for no text at back button at navigation bar

I have the following code for letting the back button in my navigation bar stay with no text:
self.navigationController?.navigationBar.tintColor = UIColor.whiteColor()
let backItem = UIBarButtonItem(title: "", style: .Bordered, target: nil, action: nil)
navigationItem.backBarButtonItem = backItem
This worked until i updated xCode. Now I get this error message:
'Bordered' was deprecated in iOS version 8.0: Use UIBarButtonItemStylePlain when
minimum deployment target is iOS 7.0 or later.
thanks for your help
Just set the style to plain. Like this
let backItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
navigationItem.backBarButtonItem = backItem
Be careful. This technique works on every device except for an iPhone 6/6s Plus!
The common answer to this question is thus:
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
Apple's documentation says to use nil for the title instead:
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: nil, style: .Plain, target: nil, action: nil)
Both techniques seem to work in all simulators and actual devices, save for the "iPhone plus". To rectify, without getting into creating your own Rects or similar cleverness, you can assign a single space to the title, as such:
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: .Plain, target: nil, action: nil)
I don't like this answer, but it does work. The answer really should be setting the title to nil, as Apple documents. I can't believe such a large company could not have tested for that!