Storyboard application using Swift.
How can I close the calendar (presented for date selection when the UIDatePicker style is .compact) after a user selects a date? Currently, the user is required to tap outside the calendar for it to close. I want it to close automatically after a user has selected a date.
I've tried resigning first responder of the UIDatePicker, ending editing in the table view controller once a notification is received that a date changed, reading another response (here) and so far nothing is working for me.
For now, you can do:
presentedViewController?.dismiss(animated: true, completion: nil)
i.e.
override func viewDidLoad() {
super.viewDidLoad()
datePicker.addTarget(self, action: #selector(dateChanged), for: .valueChanged)
}
#objc private func dateChanged() {
presentedViewController?.dismiss(animated: true, completion: nil)
}
But note that this will potentially break in future iOS versions as this relies on the implementation detail that compact date pickers presents a view controller to show the calendar. If you print out the type of the presented VC, you'll see _UIDatePickerIOSCompactViewController. The leading _ suggests that this is an implementation detail.
All that is documented about compact is:
A style indicating that the date picker displays as a label that when tapped displays a calendar-style editor.
So in future versions of iOS, they could instead change the implementation to not present a new view controller, and this could very well break, and you'll have to find another way then. I see no documented way of doing this right now.
Related
I am writing a very simple game that is designed specifically for blind users, but may also be used by sighted users. It uses many buttons as elements, however, blind users interact with these buttons through custom gestures (pan, tap, etc), so standard voiceover interaction is not appropriate.
The issue lies in the fact that there are no accessibility objects on the screen at all, so whenever the game loads, voiceover starts reading the labels on buttons (e.g. "Possible text: back, menu...). These buttons are read regardless of the fact that they are not enabled. I also can't remove most of them from the view for blind users.
I have tried turning off accessibility for the elements, unchecking "button" from accessibility traits, everything has allows direct interaction selected, I have tried .accessibilityElementsHidden, all the suggestions from How do you exclude a UIButton from VoiceOver? and nothing seems to work.
My current solution has a clear UILabel with no text in it, this is set to the only item in the .accessibilityElements array, and then for good measure I post an accessibility screen changed notification with that label as the object so it becomes focused, then I wait a second in a dispatch queue async after call, remove the label entirely, and set focus back to the main view so the user can interact.
Here is an example of my current solution:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.accessibilityElements = [lblVoiceOver!]
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIAccessibility.post(notification: .screenChanged, argument: lblVoiceOver)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.lblVoiceOver.removeFromSuperview()
UIAccessibility.post(notification: .screenChanged, argument: self.view)
}
}
This is a silly hack, at best, and I would love to implement a real solution that simply prevents the "Possible text" from being read by voiceover. I believe the possible text feature was added in iOS 11, to help apps that are not written with accessibility in mind to be more accessibility friendly, but so far I haven't found a way to turn this off.
The issue lies in the fact that there are no accessibility objects on the screen at all.
If you want to reach this purpose, just write self.view.accessibilityElementsHidden = true in your view controller that will contain no accessible element for VoiceOver anymore: this will indicate that the content of your container isn't accessible.
blind users interact with these buttons through custom gestures (pan, tap, etc), so standard voiceover interaction is not appropriate [...] I would love to implement a real solution that simply prevents the "Possible text" from being read by voiceover.
... following the preceding rationale, you should prevent VoiceOver from analyzing and reading anything in your view.
Now, dealing just with your buttons, I created a blank project with a simple code for the view controller hereafter (Swift 5.0, iOS 12):
import UIKit
class NonAccessibleButtonVC: UIViewController {
#IBOutlet weak var aboveLabel: UILabel!
#IBOutlet weak var belowLabel: UILabel!
#IBOutlet weak var myButton: UIButton!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myButton.accessibilityElementsHidden = true
}
}
... and you get the following result on your device:
The button isn't taken into account as an accessible element and if it doesn't work in your code, it means that anything else prevents this correct operation. Add maybe the setAccessibleElement button property value to false according to your context ?
However, another solution could be defining the display of the desired accessibility objects in your view thanks to its accessibilityElements property taking away the buttons for instance (see Example 2 of this link): that will definitely work in addition to ordering all your elements.
I am trying to refresh my viewcontroller (reload it ) using UIApplication.shared.keyWindow?.rootViewController as shown below in code, it works however when reloaded the novaigation controller is not there, I mean there a back button that can go to previous view controller it does not appear when I did that - any help
#IBAction func Save(_ sender: AnyObject) {
UIApplication.shared.keyWindow?.rootViewController = storyboard!.instantiateViewController(withIdentifier: "UploadPhotoviewcontroller")
}
Default back button, appearance and work in current navigationController navigation stack. If you change rootViewController, this action will destroy you current navigation stack and all previous controllers. And you cant go back.
Change the rootViewController be bad practic. I think you have a problems with the architecture
#jon when I try to comment it did not and I can not reply to your question as comment - I do not know why
Exactly that is the reason
I found the answer:
To deselect the picked image in elcimagepickercontroller use below code:
imagePicker.popToRootViewController(animated: true)
self.dismiss(animated: true, completion: nil)
I'm new to iOS developing, any help or resources are appreciated!
Is there a way to search for a city, town, state or country using a search bar without having the map visible? I basically want to have the customer type in any location, select it and then view search results in that location that the client added on their controller/log in. I've already created the log in/sign up, eventually I want to integrate Stripe for each search result.
I've tried looking for tutorials online, but only seem to find tutorials based off of having the actual Map visible and inputting regular text.
Yes, you can initialize a uisearchbar without tying it to a map. You then make your class a delegate and tell it how to handle searches. The following is in Swift 2
First, make your class a delegate:
class yourClass: UISearchBarDelegate
Initialize the controller
var searchController: UISearchController!
searchController = UISearchController(nil) // nil means you want to show the results on the same page as the search
Then, in whatever action will present the search:
self.presentViewController(searchController, animated: true, completion: nil)
Now you tell it how to handle a search using one or more of these methods:
func searchBar(UISearchBar, textDidChange: String) //the user changed the search text
func searchBarTextDidEndEditing(UISearchBar) // user stopped changing search text
func searchBarSearchButtonClicked(searchBar: UISearchBar) // user clicked "Search"
According to the docs:
At a minimum, the delegate needs to perform the actual search when text is entered in the text field.
So using any of those methods should meet the minimum requirement
See here for the full docs
Currently on my viewController : Upload, my button send the data to my database only if all the information are filled out, and I come back to the preview view (table View) with :
self.navigationController?.popViewControllerAnimated(true)
I would like, if it is possible, to come back to my main view on the tabBarController. I tried many things, like directly on the storyboard with Present modally segue to "TabBar controller", but I come back to the TabBar without sending my data to the database and without checking in the information are filled out..
How can I do it?
Thanks!
UITabBarController has a property selectedIndex with which you can switch the selected tab. So on completion after dismissing the UploadViewController you can run:
self.tabBarController?.selectedIndex = 0 // Index to select
It would probably be best to create a delegate for your UploadViewController to fire a function to do all the work in your previewVC on API call completion.
(Super late response...in case someone has similar questions, presumably in later version of Swift, such as mine which is Swift 5, iOS 13.2). Steps:
Be sure to set an id for your UITabBarController storyboard, e.g. "TabBarViewController"
Next, add the following to an action that has already been connected to a button:
let ID_TABBAR = "TabBarViewCOntroller"
#IBAction func returnToTabbar(_ sender: Any) {
let tabBarController = self.storyboard?.instantiateViewController(identifier:ID_TABBAR) as! UITabBarController
self.navigationController?.pushViewController(tabBarController, animated:true)
}
Referenced from one of the responses from this post.
Update: In case your Tab Bar View Controller also happens to be the root view controller, the two lines of the code in the returnToTabbar method above can be:
self.dismiss(animated:true, completion:nil);
self.navigationController?.popViewController(animated:true);
(ref.: See answer here, for Swift4 but works just fine in Swift5)
I thought that Today View every time when i open it it calls "viewWillAppear" but its not. When i change something in my app, and then I slide down for Today View it sometimes refresh the view and sometimes not.
I do all logic in viewWillAppear (fetch data from coreData and put that data to labels), but its not called everytime.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
fetchContent()
setLabels()
setContentHeight()
tableView.reloadData()
print("view will appear")
}
how to call fetchContent and setLabels every time when user opens Today Extensions?
For this matter you should be using NSWidgetProvinding's widgetPerformUpdateWithCompletionHandler.
Steps:
1.- Make sure that your UIViewController implements NCWidgetProviding
class MainViewController: UIViewController, NCWidgetProviding
2.- Add the following function:
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.NewData)
}
3.- In your case you will be using .NewData.
Just make sure that you retrieve needed data and refresh your views (putting every data in place, filling labels, graphs, etc).
Nevermind the fact that your view is not visible during the call to this function, iOS will fill the view and take a snapshot of it.
Then that's what it shows while you are opening notification center and until you gain control again of your app.
So, in your case would be something like this:
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
fetchContent()
setLabels()
setContentHeight()
tableView.reloadData()
completionHandler(NCUpdateResult.NewData)
}
Swift 2.1 && Xcode 7.2
It looks like some bug appears when you many time recompile this today extension. solution is to remove from notification center and add it again. then it refresh fine everytime opened