UIDatePicker mode countDownTimer value changed callback not calling first time - ios

I am having this strange issue in UIDatePicker mode .countdownTimer
When I add constraints of up/down/left/right to the datePicker view, the callback is not registered for the first time. However, if I add only Alignment constraints, the callback works everytime. Maybe its a bug. Any idea why isn't it working?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var datePicker: UIDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
datePicker.datePickerMode = .countDownTimer
datePicker.addTarget(self, action: #selector(datePickerValueChanged(_:)), for: .valueChanged)
}
#objc func datePickerValueChanged(_ sender: UIDatePicker){
print("Selected value \(sender.countDownDuration)")
}
}

OK, it's a long-known bug in iOS, since iOS 7 or so.
You can work-around in your case if you put the following somewhere into viewDidLoad:
DispatchQueue.main.async {
self.datePicker.countDownDuration = self.datePicker.countDownDuration
}
Seems nasty, and is nasty.

The workaround of setting the countDownDuration didn't work for me. Instead the only way I managed to solve this is to replace the UIDatePicker with a UIPickerView and populate it myself with 4 components: hours, "hour(s)" label, mins, "min(s)" label.
The hours are 0-23
The hours label is a single row component, which
gets reloaded when the hours are changed so that it switches to
"hour" when it's 1 hour
The mins are 0-59
The mins label is the same
as the hours label

I was able to fix this issue in SwiftUI (using a "UIDatePicker" created with "UIViewRepresentable") by appending an "onAppear" to the picker and inside the closure updating the state variable used for tracking the duration to any value that is not the default value. This forces a programmatic update on the first render which then allows the picker to work correctly on the first spin.

Related

Update Label Text with Timer Values

I know there are a lot of answers out there about similar issues, but it seems all of them are don't fit to my problem. And I am new to Swift and my head is burning about all this stuff.
The task is very simple. I have a ViewController and a Class called "TimeIsTicking", which I defined in a separate Swift File. I did this, because I want to feed 5 ViewController (which are nested in TabBarController) with data from the Timer and they all have to run "synchronized".
The function fireTimer puts every second the value of 1 to the variable seconds.
In the ViewController is a Label and I want the Label to be updated every time when the timer puts a new value to seconds.
dayLabelText gets the data, here is everything fine, but from that point I'm stuck. Label Text isn't been updated.
I suspect, that there has to be a "loop" to reload the data for the Label and I thought the loop in fireTimer would be enough but I was obviously wrong.
I tried the "Observer Thing" and the "Dispatchqueue Thing" but I didn't play well ((obviously).
Help would be much appreciated.
Here is the code of the timer class:
import Foundation
import UIKit
class TimeIsTicking {
var seconds: Int = 0
static let timeFlow = TimeIsTicking()
func fireTimer() {
let finance = FinanceVC()
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
self.seconds += 1
finance.dayLabelText = "\(self.seconds)"
print("Seconds: ", self.seconds)
print("LabelText: ", finance.dayLabelText)
}
}
}
And here is the code of the VieWController:
import UIKit
class FinanceVC: UIViewController {
#IBOutlet weak var dayLabel: UILabel!
var dayLabelText = String(TimeIsTicking.timeFlow.seconds)
override func viewDidLoad() {
super.viewDidLoad()
dayLabel.text = dayLabelText
TimeIsTicking.timeFlow.fireTimer()
}
// Do any additional setup after loading the view.
}
I see a few issues:
The finance object goes out of scope when fireTimer() returns. It will be deallocated then. The timer will be setting a label text on a no longer existing view controller.
Instantiating a UIViewController with FinanceVC() doesn't display it on screen. After instantiating you need to explicitly show. You can do this for example by calling present(_:animated:completion:) from a parent view controller.
The timer updates dayLabelText which does not update dayLabel.text
Might be good to follow a basic YT tutorial on how to display a view controller.
Good luck, you'll get it soon enough!

How to disable UIControlEventEditingChanged

I have a UIViewController with several UITextFields. When tap one text field, it should present the barcode scanning view controller. Once the scanning is completed, my barcode scanning viewcontroller is disappearing (used "dismissViewcontroller") and the scanned value should entered into the text field I tapped. This is working fine. I have set the delegate for each text field like this.
[field addTarget:metrixUIViewControllerIn action:#selector(executeScriptOnTextFieldChange:) forControlEvents:UIControlEventEditingChanged];
The problem is this :
Lets say I have set an alert to display inside this executeScriptOnTextFieldChange method. Once I tapped on the 1st text field, then the barcode scanner comes. Once I scanned barcode scanner closes and set the value for the first text field and fire the alert.Thats ok. But then if scanned by tapping the 2nd textfield and the string will set to that textfield and fire the alert related to 2nd textfield also fire the alert related to first textfield as well. I want to stop happening this. Is there any way to disable the delegate for one textfield? This happens because I am refreshing the view in the viewDidAppear. But I have to do that as well. Please help me.
UIControlEventEditingChanged for a textField can fire at many different events that are not even directly related to that textField, but related inderectly.
For instance, when your ViewController is presenting the barcodeScanner it may trigger a "resignFirstResponder" event on the textField. Also when the 2nd textField is tapped, cause the 2nd becomes first responder and the 1st suffers a "resignFirstResponder".
I suggest trying to use a UITapGestureRecognizer in your textField instead. Example:
Swift 4
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.textField.tag = 1
self.textField.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(fireTextField(_:))))
}
#objc func fireTextField(_ sender: UIGestureRecognizer){
let view = sender.view
guard view != nil else{
//Do nothing
return
}
let condition = view!.tag == 1
if condition{
//or do whatever other stuff you need
self.textField.becomeFirstResponder()
}else{
//Whatever for other textFields
}
}
This way, you could use the "tag" attribute to determine which textField is firing and so adjust "condition". You could also filter the flow with a switch using the "tag".
Not sure if any of this will really help as I would need more info about the flow you need to accomplish. Hope it does help!

UIButton.setTitle | Huge delay when changing the title [duplicate]

in my app I want to set the text of an UILabel. The text comes from a JSON-object. I add my UILabel to my storyboard, set the IBOutlet and call my async-method to get my JSON-object.
In the response-method I set the text of the UILabel. But the text change needs some seconds.
When the response comes I print it to the console. There I can see, that the delay doesnt comes from the async-method.
The response comes, I can see it in the console. Wait some seconds than the UIlabel changes.
I dont understand this behaviour, is there a trick to refresh the UIlabel instantly?
some code:
#IBOutlet weak var label_news: UILabel!;
override func viewDidLoad() {
super.viewDidLoad()
self.label_news.text = "CHANGE";
rcall.GetNews_GET_NewsResponse_0(self.NewsResponseHandler);
}
func NewsResponseHandler(resp:NewsResponse!){
self.label_news.text = resp.NewsText;
println(resp.NewsText);
}
Sorry if this is a beginner question, swift and storyboards are totally new for me.
best regards
Like what Rob stated in the comment, all UI changes need to be done on the main thread. I haven't implemented it in Swift yet, but the Objective-C and what I'm assuming would be the Swift is below...
Objective-C
dispatch_async(dispatch_get_main_queue(), ^{
self.label_news.text = resp.NewsText;
});
Swift:
dispatch_async(dispatch_get_main_queue()) {
self.label_news.text = resp.NewsText;
}
c_rath's answer is correct. In swift 3 the syntax was changed (yet again) to
DispatchQueue.main.async {
self. label_news?.text = resp.NewsText
}

How to code date of birth range to cause an action?

I am trying to build a zodiac sign app for class. I am using a UIDatePicker and an UIImageView to display the image once the user picks a date (date range, say if the date of birth is between a certain date). The zodiac will be revealed in text and also the image appears. So far I have my user interface and all the images ready, but I am not sure how to go with the main code for it to function properly.
var zodiac = ["Aqurius", "Pisces", ...]
override func viewDidLoad() {
zodiacSign.text = zodiac[0]
}
Add an #IBAction by ctrl-dragging from your UIDatePicker in Interface Builder to your view controller.
#IBAction func datePickerChanged(sender: AnyObject) {
let date = datePicker.date
// configure your labels and images according to date
}

iOS UIDatePicker always returns today's date

There is similar question on Stack Overflow, but the problem there was different then mine, so I feel free to open this one.
I have iOS view controller with a UIDatePicker defined in storyboard.
I have defined(connected) IBOutlet in view controller code like this:
#IBOutlet weak var endTimeDatePicker: UIDatePicker!
At one point I set initial date like this:
endTimeDatePicker.date = state!.endTime;
or like this:
endTimeDatePicker.setDate(state!.endTime, animated: true);
and it shows correct date indicating that date picker is connected correctly.
But then, if I pick another date and try to get selected date with endTimeDatePicker.date it always returns the same - today's date, no matter what I pick.
Storyboard properties for date picker are:
Mode - Date and Time
Interval - 1 minute
Date - Current Date (but it's the same with custom, only returning defined custom date every time)
Is there something that I've missed to do?
I don't have for sure two different datePickers (like in potential duplicate Stack Overflow questions)
EDIT: looks like it only happens when Date Picker has set initial value from code.
EDIT: code that sets Date Picker:
private func reloadData (state : State?)
{
if (state != nil)
{
endTimeDatePicker.setDate(state!.endTime, animated: true);
backgroundImage.image = UIImage(named: state!.type.imageName)!;
}
else
{
let endDate = NSDate(timeIntervalSinceNow: 1800);
endTimeDatePicker.setDate(endDate, animated: true);
backgroundImage.image = UIImage(named: StateType.Active.imageName);
}
}
Code that tries to read selected date:
#IBAction func doneTapped(sender: AnyObject) {
let row: Int = self.stateTypePicker.selectedRowInComponent(0);
state = State (id: -1, type: stateTypes[row], startTime: NSDate(), endTime: self.endTimeDatePicker.date);
service.addState(state!) {
(responseDict) in
}
}
func reloadData is called in callback for http request. Could be thread lock problem maybe?
Your issue is related to the callback method reloadData from the NSURLSession. In here, you are updating the UIDatePicker in a background thread, but all UI updates have to be done on the main thread.
This is the reason why you are seeing todays date, when you are reading the date property of the UIDatePicker.

Resources