问题
I created a class than makes implementing a picker view easier for the developer. In the UIPickerView Datasource methods, I'm returning the values from a closure. Example:
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerNumberOfRows!(component: component)
}
I am using the same thing for all datasource methods. When I run the app, it shows the picker on screen with no data. The problem is I'm receiving data from the closure which in turn are not running on the main thread.
Is there a way to run that closure on the main thread and make sure it returns a value suitable for the datasource function return value type? (ex, from the example above, the closure will return an Int)
EDIT:
I'm aware of using dispatch_async. However, in my case it will not help me. Let's say I created a function that runs the closure on the main thread and needs to return the value it will look something like this:
func closureOnMainThread()->Int{
dispatch_async(dispatch_get_main_queue()) {
// Run your code here
return self.pickerNumberOfRows!(component: 0)
}
return 0
}
However, There is a problem in the function above which is returning 0 before return the value inside the dispatch block.
Edit 2: To make things clear. My code contains a custom class for the picker and a UIViewController implementing it:
class CustomPickerView: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
var pickerNumberOfRows: ((component: Int)->Int)?
//MARK: UIPickerViewDatasource Methods
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerNumberOfRows!(component: component)
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return pickerNumberOfComponents!()
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {print("Hello")
return pickerRowTitle!(forRow: row, inComponent: component)
}
//MARK: UIPickerViewDelegate Methods
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
pickerDidSelect!(row: row, component: component)
}
}
UIViewController (View Did Load):
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let picker = CustomPickerView(parent: self)
picker.pickerNumberOfRows = { component in
return 10
}
}
Please note I removed allot of accessory functions in between so that you focus on the main code part.
The developer can pass in the logic he wants in variables of closures.
回答1:
Hope I undestand currently. In this block you can run any code, and it will execute on the main Thread.
dispatch_async(dispatch_get_main_queue()) {
// Run your code here
}
You can run this to know if you are on main thread or not:
NSThread.isMainThread()
EDIT
What you are looking for is, insted of returning a sync response : ->Int
You should return a block, async response:
func someLongProcessTaskAndReturnCompenents(completion: (components: Int)->Void){
//Here you can do your long process task on back thread...
dispatch_async(dispatch_get_main_queue()) {
completion(self.pickerNumberOfRows!(component: 0))
}
}
Now when you run it, the response will return synchronized:
someLongProcessTaskAndReturnCompenents { (components) in
//Your logic after response
}
回答2:
You cannot put dispatch_async in closureOnMainThread like that. It is a async process so it will return 0 defined outside of GCD block. SO there must be some issue inside closure code.
Its has to be synchronouse not asynchronous.
So try some this like this. - This is just a sample code block only. Assuming pickerNumberOfRows will return a Int on same thread.(I used dispatch_sync instead of dispatch_async)
func closureOnMainThread()->Int{
var x = 0;
if (NSThread.isMainThread() == true) {
x = self.pickerNumberOfRows!(component: 0)
} else {
dispatch_sync(dispatch_get_main_queue()) {
x = self.pickerNumberOfRows!(component: 0)
}
}
return x
}
回答3:
Okay, the problem was not even related to threading/closures. The problem was in using let or var in the view controller where I instantiating the custom picker. Here is a new view controller implementation:
var picker: HBPickerView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
picker = HBPickerView(parent: self)
picker!.presentPicker()
}
Thanks for everyone who shared their answers.
来源:https://stackoverflow.com/questions/38762919/run-closure-on-main-thread