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
Related
I was trying to implement viper architecture on my Xcode. I am following an article https://medium.com/cr8resume/viper-architecture-for-ios-project-with-simple-demo-example-7a07321dbd29.
also, I downloaded the article source code, and run well also UI is changing. but when I created a new project with swift 5 and copy all methods and classes. after running UI is not updating but both codes are the same. please check below
This is a single-page sample code
please check this Github for my project https://github.com/Faizulkarim/movieHut/tree/main/MovieHutWithViper
This is my protocol
protocol PresenterToViewProtocol: AnyObject{
func showMovieList(MovieList: Array<movieModel>)
}
Here o call showMovieListMethod
extension MoviePresenter : InteractorToPresenterProtocol {
func movieFetchSuccess(movieModelArray: Array<movieModel>) {
view?.showMovieList(MovieList: movieModelArray)
}
}
// on MovieListViewController confirmed delegate
extension MovieListViewController:PresenterToViewProtocol{
func showMovieList(MovieList: Array<movieModel>) {
self.Movies = MovieList
self.cover.backgroundColor = UIColor.red
print(self.movies.count)
self.trandingTableView.reloadData()
}
}
on showMovieList function call, it's printing the total count of movies. But when I reloadtableview() table view is not interacting is showing any data. Even when I set to change the background of a view it's not changing. when I set breakpoint is' stop. So protocol is called properly but UI is not updating. i search in google didn't find relevant answer.
You might be in a background thread. You can only update UI from main thread.
Try to update like this:
DispatchQueue.main.async {
self.trandingTableView.reloadData()
}
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
a newbie question :)
I'm trying to use Twiiter Digits for authentication (by phone number) in my (first) iOS app.
It is easier for my to understand how to position a button programatically when it is a button that i create. but this 1 line of code confuses me.
to embed their action button i just need to add this part of code (see documentation):
override func viewDidLoad() {
let digitsButton = DGTAuthenticateButton(authenticationCompletion: { (session, error) in
// Inspect session/error objects
})
self.view.addSubview(digitsButton)
}
My problem is that the creation of this button is automatically and have a completion handler, so when\where exactly do i have the option to position (format) it?
Thanks.
You can always create your own custom button and use the methods of Digits to perform the same actions. For example :
func didTapButton(sender: AnyObject) {
let digits = Digits.sharedInstance()
digits.authenticateWithCompletion { (session, error) in
// Inspect session/error objects
}
}
Also if you want to continue to customize your Digits button and it's View Controllers , you can find more here here.
It might sound strange but it took me some time to realize i can\need to do it after the completion handler (inside the ViewDidLoad...).
Thanks for Letting me know i can use my own button with Digits.
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
}
I want to update the title property of a user interface element on iOS using Swift. In this case it is a UIBarButton, but it could be a UILabel, UIButton or whatever. Currently I am using this code, which works:
func setStatusMessage(barButton: UIBarButtonItem) {
let currentVersion = StatusModel.getCurrentVersion()
var statusUpdates = [StatusModel]()
var statusForCurrentVersion: StatusModel!
var statusMessage = String()
// check if update required before setting the text
checkIfLocalStatusNeedsUpdate()
barButton.title = getLocalStatusMessage()
// Try to update status anyway...
getStatusFromRemoteSource { (statusUpdates) -> Void in
for status in statusUpdates {
if status.version == currentVersion {
statusForCurrentVersion = status
}
}
self.saveStatusFromRemoteSource(statusForCurrentVersion)
barButton.title = statusForCurrentVersion.message
}
}
Although effective, this solution is ugly too as it does require a user interface (view) element to be embedded in my model. Not exactly a sort of MVC beauty.....
I cannot simply use return because the local status will be returned before the remote status can be fetched. So I guess I need some kind of handler / listener / delegate (/*getting lost here*/) to update the view dynamically. In this case that means: set the title using the locally stored value and update it if a remote value is received.
What is the best way to approach this scenario in a MVC compliant way, removing UI elements from the model code (thereby increasing reusability)?
One intermediate step you could do is going over the controller to the UI, by replacing the barButton parameter with a reference to the controller
barButton.title = statusForCurrentVersion.message
->
self.controller.update(title: statusForCurrentVersion.message)
This way your code is allowed to grow (updating more labels etc), but it comes with a cost of more code and harder readability.