Make UIDatePicker appear with the time when a static UITableViewCell is tapped - ios

I have an app where I want, by tapping a static UITableViewCell within a UITableViewController, a UIDatePicker to pop up on the bottom of the screen, that lets me pick the time(e.g. the hour, minute, and AM/PM).
Obviously, it would have to be created completely programmatically because
I want to hide it(when a button on the picker is pressed or
somewhere outside the picker is tapped)
I can't add it to the View Controller. Any ideas?

Apple implements similar functionality in their DateCell example, as * Islam Q.* mentioned in a comment on your original question. However, for this answer, I'll assume you'd rather do what you described.
Your goal can actually be achieved with very little code.
First, drag a UIDatePicker out onto a view controller in your
Storyboard.
Then, connect that date picker to an IBOutlet in the
implementation of your view controller subclass.
#IBOutlet weak var datePicker: UIDatePicker!
In that same view controller's viewDidLoad() (or viewDidAppear(:), if that's more appropriate), move the date picker below the bottom edge of the screen by changing its frame. Don't animate the frame change. Nothing's onscreen anyway.
override func viewDidLoad() {
super.viewDidLoad()
self.datePicker.frame = self.offscreenFrame
// ...
}
Next, in tableView(_:didSelectRowAtIndexPath:), animate datePicker back onto the screen. Right before you perform the animation, make sure to set the date picker's date to the current date.
self.datePicker.date = UIDate()
// now do the animation
Finally, make sure to animate the dismissal of the date picker when appropriate (in tableView(_:didDeselectRowAtIndexPath:), for example).
UIView.animateWithDuration(0.2) { () -> Void in
self.datePicker.frame = self.onscreenFrame
}
Note that the actual implementation of each of these bullet points can differ depending on your situation. Also, offscreenFrame and onscreenFrame are "imaginary" variables that I used to simplify the code snippets.

Related

Picker View uncaught exception SWIFT

Hello I am a beginner at SWIFT and I have an issue with my project : I have a picker View on one of my View controller and I think that I have connected all outlets but when I execute the code and test my app on the emulator it comes an error when I click on the button (on the previous View) that has to open the view in which my picker View is set. When I get rid of my the picker View from the View controller, there is no more error.
The fact is that I have watched every tutorials on PickerView and did all they shew ...
I don't know where comes from my mistake.
I let some screen shots to be clearer
The first problem is that you don't store the created picker view instance. You instantiate it inside of a function, assign the delegate and dataSource and then you don't store it in your class. So the ARC (Automatic Reference Counting) releases it, because it thinks the instance is not longer needed. Just create a variable in your PickerController and store as long as the view controller is alive.
The second problem is that you actually want to see the picker view, so you need to add it to the view controller's view. You might need to position it right or use layout constraints (Search for Auto Layout).
var pickerView: UIPickerView!
func createPickerView() {
pickerView = UIPickerView()
pickerView.delegate = self
pickerView.dataSource = self
view.addSubview(pickerView)
}

Swift3 center UIbutton

ok, I have added a button to the .swift storyboard, I have then added the following code to my ViewController.swift
#IBOutlet weak var HelloButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
HelloButton.frame.origin.x = view.frame.size.width / 2
}
However when I test it on my iPhone 5s it moves it to the right of my screen and does not make it centered. I am unsure what I need to do to make the button centered.
Yes this is my first helloWorld app, and maybe over my head, but I understand web programing, and I understand to make it center automaticilly I need to have the screen view width total and the button width. I then need to say to the button to divide the screen view width by 2.
for example in CSS we do this
#button{margin:0 auto;}
but this is not HTML or CSS. IT'S SWIFT3
Try this:
HelloButton.center.x = view.frame.width/2
Yours is not correct because the origin.x is the first point (top left) of the button view, not the center of the view, you can change it's background color to different color to see it
Also, the variable name should be lowerCase, followed by code convention
You should be using Autolayout for accomplishing what you want and the way you do is like the following in your ViewController inside of Main.storyboard: (control + left mouse click -> dragging the blue line upwards and then releasing the control and left mouse click, you will see the popup)
In the popup select the options:
Center Horizontally In Container
Center Vertically in Container
But if you want to do it programmatically, do it in viewWillLayoutSubviews method:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewWillLayoutSubviews() {
super. viewWillLayoutSubviews()
button.center = view.center
}
}
In your project Autolayout is enabled? if Auto Layout enabled then Change UI in viewdidLoad: may not give appropriate result. use -viewWillLayoutSubviews method to do UI task, it you want to center your button
myButton.center = self.view.center
Hope it will work.
Add the code to viewDidAppear that way the actual view has shown up. Also, you should use button.center.x instead of button.frame.origin.x if you want to center it

UITextField inputView - IBActions not fired

I want to replace the default keyboard of a UITextField with a custom keyboard. So I created a new subclass of a UIViewController with a xib-file (the other way like creating both files seperately and setting the File's Owner doesn't work either).
Then I added a button to the KeyboardView and connected it to an IBAction. After that I set the textfields inputView to the new view like this:
override func viewDidLoad() {
let keyboardVC = KeyboardViewController()
textField.inputView = keyboardVC.view
keyboardVC.view.autoresizingMask = .FlexibleHeight
keyboardVC.delegate = textField
}
It's working and the custom keyboard shows up, but if I touch the button, the IBAction is not called. What's the problem in my setup? (I checked some examples and they all do it the same way).
UPDATE:
I now removed the ViewController and subclassed the UIView. Now the actions are working. Why isn't it working with a ViewController?
Since no one holds the UIViewController- there is no reference to it after the viewDidLoad() ended, it is released from the memory.
When the button is pressed, the view controller that should response to the action is not exist -> you are holding only the view of the view controller as the textField.inputView.

Get a UIButton's tag from the UIButtons which are in the popover presented by this UIButton

The headline seems lengthy but what I'm trying to do is quite simple.
I have a couple of identical buttons lined in a row and set their tags to 0...n. Clicking on any of them (the 2nd for example) would bring up a popover view in which there are several buttons representing different options (A, B, C, D). What I want to do is to turn the 2nd Button's title to B if we click on option B.
The problem is that the popover view does not know which Button presented it, since all popoverViews are instances of the same UIViewController class. So I am thinking of distinguishing the n buttons by setting their tags to different values. However, I don't know how to get the UIButton's tag from a button inside the popover this UIButton presented.
Many thanks in advance!
This is how I will solve this in swift. I will declare a delegate in the popoverViewController and have a method e.g
protocol popOverViewControllerDelegate: class {
func popOverViewController(controller: PopOverViewController,
didSelectItem buttonTitle: String)
}
then I will add a target action to the UIButton in the popOver
button.addTarget(self, action: "selected:",forControlEvents:.TouchUpInside)
the select method will have sender passed to it
func selected(sender:UIButton){
delegate?.popOverViewController(self, didSelectItem: sender.currentTitle)
//dismiss viewcontroller
}
remember to declare
weak var delegate: popOverViewControllerDelegate!
now have the viewcontroller that called the popOver, subclass to this delegate and implement it's method. Whenever a button in the popOver is selected, this method will be called and the currentTitle of the method will be passed. You can now use that to set the currentTitle of the button that called the popOver.
In case you need further help with the last part, please ask.
I've fixed the problem by adding a property tagNumberso that after instantiating the popoverViewController's class, I set the instance's tagNumber to the sender's tag. Then I send the tagNumber back together with sender.currentTitle. This way, the presenter of the popover could know both the title and tag number of the UIButton.

presenting a modal view controller while the keyboard is active

So I basically have a form, consisting of several text fields. The user types into the fields as usual. But the user also has the option of double-tapping a text field, which presents a modal view controller, allowing the user to choose from a number of options relating to that field.
Can I somehow present the modal "over" the keyboard, such that when it is dismissed, the keyboard is still active for the field that had been first responder before I presented the modal?
Right now, the keyboard dismisses while the modal appears, and reappears as the modal is dismissed. It looks clunky to me, and distracting. Would love to streamline it, and reduce the amount of animation onscreen.
Edit: I've updated this answer for iOS 12 and Swift. The revised example project (containing new Swift and updated Objective-C implementations) is here.
You can create a new UIWindow and place that over the default window while hiding the keyboard's window.
I have an example project on Github here, but the basic process is below.
Create a new UIViewController class for your modal view. I called mine OverlayViewController. Set up the corresponding view as you wish. Per your question you need to pass back some options, so I made a delegate protocol OverlayViewController and will make the primary window's root view controller (class ViewController) our delegate.
protocol OverlayViewControllerDelegate: class {
func optionChosen(option: YourOptionsEnum)
}
Add some supporting properties to our original view controller.
class ViewController: UIViewController {
/// The text field that responds to a double-tap.
#IBOutlet private weak var firstField: UITextField!
/// A simple label that shows we received a message back from the overlay.
#IBOutlet private weak var label: UILabel!
/// The window that will appear over our existing one.
private var overlayWindow: UIWindow?
Add a UITapGestureRecognizer to your UITextField.
override func viewDidLoad() {
super.viewDidLoad()
// Set up gesture recognizer
let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
doubleTapRecognizer.numberOfTapsRequired = 2
doubleTapRecognizer.delegate = self
firstField.addGestureRecognizer(doubleTapRecognizer)
firstField.becomeFirstResponder()
}
UITextField has a built-in gesture recognizer, so we need to allow multiple UIGestureRecognizers to operate simultaneously.
extension ViewController: UIGestureRecognizerDelegate {
// Our gesture recognizer clashes with UITextField's.
// Need to allow both to work simultaneously.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
This is the interesting part. When the gesture recognizer is triggered, create the new UIWindow, assign your OverlayViewController as the root view controller, and show it. Note that we set the window level to UIWindowLevelAlert so it will appear in front. However, the keyboard will still be in front despite the alert window level, so we have to manually hide its window, too.
It is important to not set the new UIWindow as key or to change the first responder from the UITextField or the keyboard will be dismissed.
Previously (before iOS 10?) we could get away with overlayWindow.makeKeyAndVisible(), but now setting it as key will dismiss the keyboard. Also, the keyboard's window now has a non-standard UIWindow.Level value that is in front of every publicly defined value. I've worked around that by finding the keyboard's window in the hierarchy and hiding it instead.
#objc func handleDoubleTap() {
// Prepare the overlay window
guard let overlayFrame = view?.window?.frame else { return }
overlayWindow = UIWindow(frame: overlayFrame)
overlayWindow?.windowLevel = .alert
let overlayVC = OverlayViewController.init(nibName: "OverlayViewController", bundle: nil)
overlayWindow?.rootViewController = overlayVC
overlayVC.delegate = self
// The keyboard's window always appears to be the last in the hierarchy.
let keyboardWindow = UIApplication.shared.windows.last
keyboardWindow?.isHidden = true
}
The overlay window is now the original window. The user can now select whatever options you built into the overlay view. After your user selects an option, your delegate should take whatever action you intend and then dismiss the overlay window and show the keyboard again.
func optionChosen(option: YourOptionsEnum) {
// Your code goes here. Take action based on the option chosen.
// ...
// Dismiss the overlay and show the keyboard
overlayWindow = nil;
UIApplication.shared.windows.last?.isHidden = false
}
The overlay window should disappear, and your original window should appear with the keyboard in the same position as before.
I can't try this right now, but have implemented similar for other purposes. In the action for presenting the modal controller, I assume gesture recognizer or delegate method, first take a screenshot and place it in an imageView over the current subviews. Later when returning, simply remove the imageView.
Might sound crazy but I remember having done this for a transition where the keyboard moving during the transition caused similar clunky behavior. It was not difficult to implement at all.
If you have trouble trying it, perhaps someone will provide some code. I can reference my own work later and add an example, but not now.
#Rob Bajorek's answer is excellent.
For iOS 9,10 there are small changes.
Instead of the code:
[self.overlayWindow setWindowLevel:UIWindowLevelAlert];
[self.overlayWindow makeKeyAndVisible];
Put the following code:
NSArray *windows = [[UIApplication sharedApplication] windows];
UIWindow *lastWindow = (UIWindow *)[windows lastObject];
[self.overlayWindow setWindowLevel:lastWindow.windowLevel + 1];
[self.overlayWindow setHidden:NO];
In order to keyboard to visible any of text accepting fields such UITextField or UITextView or UISearchBar should be the first responder and they should be visible in the view. Meaning responding view should be in the top level hierarchy in the window.
If you don't need this effect, Instead of presenting a ViewController you can add ViewController.view as a subview of your self.view with animation.
You have access to the frame of the keyboard in iOS.
You need to implement code to listen to the keyboard notifications (like UIKeyboardWillShowNotification and UIKeyboardWillChangeFrameNotification). The notification will send you informations about the frame of the keyboard.
Giva a look to the description of the "Keyboard Notification User Info Keys" in the windows reference.
You'll find useful for you purpose:
UIKeyboardBoundsUserInfoKey The key for an NSValue object containing a CGRect that identifies the bounds rectangle of the
keyboard in window coordinates. This value is sufficient for obtaining
the size of the keyboard. If you want to get the origin of the
keyboard on the screen (before or after animation) use the values
obtained from the user info dictionary through the
UIKeyboardCenterBeginUserInfoKey or UIKeyboardCenterEndUserInfoKey
constants.
With the information of the keyboard frame you can show there you modal view.
Just add an tap gesture in your textfield and a UITextfield *flagTextfield;
UITapGestureRecognizer* doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(DoubleTapMethod:)];
doubleTap.numberOfTapsRequired = 2;
[self.txtTest addGestureRecognizer:doubleTap];
-(void)DoubleTapMethod:(UITapGestureRecognizer *)gesture
{
[flagTextfield resignFirstResponder];
NSLog(#"DoubleTap detected");
//Set your logic on double tap of Textfield...
//presents a modal view controller
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
flagTextfield = textfield;
}

Resources