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

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
}

Related

Updating a UILabel Through A Function....Is There Something I'm Missing?

I asked a question similar to this one earlier but this question is more about the general language and fundamentals of Swift. In the code below, shouldn't this function technically work and change the label text? I've been running it for a while now and every time it fails. I've made sure that all of my outlets are linked properly as well. Sorry if the answer is obvious, I'm new to Swift.
func changeLabel() {
DispatchQueue.main.sync(execute: {
self.testText.text = "YES"
})
}
override func viewDidLoad() {
super.viewDidLoad()
let city:String? = nil
if city == nil {
changeLabel()
}
}
viewDidLoad is always called from the main thread (unless a programmer mistakenly does otherwise - and that's a whole other problem).
So there is no point to using DispatchQueue.main.sync to update the label. In fact, it's bad in this case. Calling DispatchQueue.main.sync when already on the main queue will cause the app's user interface to hang until the app is killed.
You have two choices:
Remove the use of DispatchQueue.main.sync since it's not needed in the code you posted.
Change sync to async. This fixes the problem with the app user interface hanging and it also allows you call the changeLabel method from any queue and work properly.
Use this instead:
func changeLabel() {
DispatchQueue.global().async(execute: {
print("teste")
DispatchQueue.main.sync{
self.testText.text = "YES"
}})
Thanks

UIView doesn't change at runtime

I've had this working in other variations but something seems to elude me in the change from objective-c to swift as well as moving some of the setup into it's own class.
So i have:
class ViewController: UIViewController, interfaceDelegate, scrollChangeDelegate{
let scrollControl = scrollMethods()
let userinterface = interface()
override func viewDidLoad(){
super.viewDidLoad()
loadMenu("Start")
}
func loadMenu(menuName: String) {
userinterface.delegate = self
userinterface.scrollDelegate = self
userinterface.removeFromSuperview() //no impact
scrollControl.removeFromSuperview() //no impact
userinterface.configureView(menuName)
view.addSubview(scrollControl)
scrollControl.addSubview(userinterface)
}
}
This sets everything up correctly but the problem occurs when I change loadMenu() at runtime. So if the user calls loadMenu("AnotherMenu") it won't change the UIView. It will call the right functions but it won't update the view. Although if I call loadMenu("AnotherMenu") at the start, the correct menu will display. Or if I call loadMenu("Start") and then loadMenu("AnotherMenu") then the menu displayed will be "AnotherMenu". As in:
override func viewDidLoad(){
super.viewDidLoad()
loadMenu("Start")
loadMenu("AnotherMenu")
}
When I list all the subviews each time loadMenu() is called, they look correct. Even during runtime. But the display is not updated. So something isn't getting the word. I've tried disabling Auto Layout after searching for similar issues but didn't see a difference.
Try adding setNeedsDisplay() to loadMenu
Eg
func loadMenu(menuName: String) {
userinterface.delegate = self
userinterface.scrollDelegate = self
userinterface.removeFromSuperview() //no impact
scrollControl.removeFromSuperview() //no impact
userinterface.configureView(menuName)
view.addSubview(scrollControl)
scrollControl.addSubview(userinterface)
view.setNeedsDisplay()
}
setNeedsDisplay() forces the view to reload the user interface.
I didn't want to post the whole UIView class as it is long and I thought unrelated. But Dan was right that he would need to know what was going on in those to figure out the answer. So I created a dummy UIView class to stand in and intended to update the question with that. I then just put a button on the ViewController's UIView. That button was able to act on the view created by the dummy. So the problem was in the other class. Yet it was calling the methods of the ViewController and seemingly worked otherwise. So then the issue must be that its acting on an instanced version? The way the uiview class worked, it uses performSelector(). But in making these methods into their own class, I had just lazily wrote
(ViewController() as NSObjectProtocol).performSelector(selector)
when it should have been
(delegate as! NSObjectProtocol).performSelector(selector)
so that was annoying and I wasted the better part of a day on that. But thanks again for the help.

Swift update UILabel with dispatch_async not working

Why doesn't this work?
dispatch_async(dispatch_get_main_queue(), {
self.timeStringLabel.text = "\(self.timeStringSelected)"
println(self.timeStringLabel.text)
})
I'm trying to update a label in Swift but the UI for the label never changes. I keep googling it, but I can't find any responses that don't use dispatch_async. What am I doing wrong?
1st Edit: I was mistaken. I'm not printing the updated text. The text never changes. It always prints out Optional("0") if that helps. The default value is 0 as defined in the Storyboard.
I have tried it with and without dispatch_async without any success. I also tried adding
self.timeStringLabel.setNeedsDisplay()
Immediately after updating the text, but that also doesn't work.
Edit 2: Here's the complete function + UILabel declaration
#IBOutlet weak var timeNumberLabel: UILabel!
#IBAction func timeNumberButtonPressed(sender: UIButton) {
println("Number Selected. Tag \(sender.tag)")
dispatch_async(dispatch_get_main_queue()) {
self.timeNumberOneButton.selected = false
self.timeNumberTwoButton.selected = false
self.timeNumberThreeButton.selected = false
self.timeNumberFourButton.selected = false
if sender.tag == 0{
self.timeNumberSelected = 0
} else if sender.tag == 1 {
self.timeNumberSelected == 5
} else if sender.tag == 2 {
self.timeNumberSelected == 10
} else {
self.timeNumberSelected == 24
}
sender.selected = true
self.timeNumberLabel.text = "\(self.timeNumberSelected)"
self.timeNumberLabel.setNeedsDisplay()
println(self.timeNumberLabel.text)
}
}
The label is clearly visible as shown in this picture. I didn't think it would be this hard to implement, but I was very wrong. I'm willing to bet it's something really simple that I'm missing.
Try adding the line
self.timeStringLabel.setNeedsDisplay()
(after the label change)
Because the code is run asynchronously, the interface-updating methods may miss the change and not display it (especially if a time-consuming bit of code occurs before the label change). Adding this code forces the interface to check for and display any changes it may have missed.
This should be used after any asynchronous task that changes the interface, as the task's running may overlap with the interface methods, resulting in a missed change.
*Thanks to the iOS Development Journal

update text field ui in swift ios

i meet a question when i'm using swift of ios SDK 8
I used a callback to fetch data background and when data fetched, i want to set a text field value and update screen.
here is my code:
#IBOutlet var txtTest: UITextField!
#IBAction func login() {
let username = "aaa";
let password = "bbb";
var res = ServiceClient.login(username, password: password){
(res:String, error:String?) -> Void in if(error == nil){
self.txtTest.text = res;
}
}
}
Run this code and the data is correctly fetched and the txtTest did not updated, but when i tap txtTest, the value will be shown. So is there any way to force update UI? or send a message to UI thread?
Same issue that everyone is having when wanting to do UI updates in Swift: do not ever update the UI in any secondary thread. And that means anything with closures. Since Swift is using closures so much, that issue is being seen a lot more but it isn't new.
See Swift Update Label (with HTML content) takes 1min for the correct way of doing things.
UIKit is not thread-safe and should only be updated from the main thread. Downloads are done on the background thread, and you cannot update UI from there. Try:
For swift 3 and swift 4
DispatchQueue.main.async {
self.txtTest.text = "Your text"
}
Related duplicate question but with specific answer
iOS label does not update text with function in Swift

Changing text of Swift UILabel

I am attempting to learn Apple's Swift. I was recently trying to build a GUI app, but I have a question:
How do I interact with GUI elements of my app? For instance, I used interface builder to make a UILabel, and I connected it to my viewcontroller by control-clicking, so that I get the #IBOUTLET thing. Now, how do I, while in my view controller, edit the text of this UILabel? To state it another way, what code can I use to programatically control the text of something on my storyboard? All methods I have found online only appear to work with a button generated in code, not a button generated on a storyboard.
I've seen code like
self.simpleLabel.text = "message"
If this is right, how do I link it with the label in question? In other words, how do I adapt this code to be connected with the IBOutlet (If that's what I do)
If you've successfully linked the control with an IBOutlet on your UIViewController class, then the property is added to it and you would simply replace simpleLabel with whatever you named your IBOutlet connection to be like so:
class MyViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
func someFunction() {
self.myLabel.text = "text"
}
}
The outlet you created must've been named by you. The outlet belongs to your view controller. self.simpleLabel means 'fetch the value of my property named 'simpleLabel'.
Since different outlets have different names, using self.simpleLabel here won't work until your outlet is named 'simpleLabel'. Try replacing 'simpleLabel' with the name you gave to the outlet when you created it.
The correct way now would be:
self.yourLabelName.text = "message"
If you have something like this for an IBOutlet:
#IBOutlet var someLabel: UILabel!
then you could set the text property just like in your example:
someLabel.text = "Whatever text"
If you're having problems with this, perhaps you're not assigning the text property in the right place. If it's in a function that doesn't get called, that line won't execute, and the text property won't change. Try overriding the viewDidLoad function, and put the line in there, like this:
override func viewDidLoad() {
super.viewDidLoad()
someLabel.text = "Whatever text"
}
Then, as soon as the view loads, you'll set the text property. If you're not sure if a line of code is executing or not, you can always put a breakpoint there, or add some output. Something like
println("Checkpoint")
inside a block of code you're unsure about could really help you see when and if it runs.
Hope this helps.
You may trying to change a UI component not in the main thread, in that case, do this:
DispatchQueue.main.async {
someLabel.text = "Whatever text"
}

Resources