Reputation: 377
I have the following code to a run method that returns a UIImage
made from a UIView
that's created in separate UIView
class file. The FlagMarkerView
is a separate subclass of UIView
and is referenced here. The problem is the line FlagMarker *markerView...
throws the following Main Thread Checker Error and crashes the app.
UIView.init(coder:) must be used from the main thread.
The code used to work as is, but no longer works as I've updated the project to target iOS 11.
I tried wrapping the FlagMarkerView
call in a dispatch_async(dispatch_get_main_queue(), ^{});
but that doesn't work because it doesn't get picked up by the return UIImage
in the method. I also tried to use a -(void)
method instead of returning a UIImage
, but that’s hazard with the complexity of my project.
Is there a way I can create the newMarkerImage
in - (void)updateMyFlagsWitAlert:
and use dispatch_async(dispatch_get_main_queue(), ^{
for FlagMarkerView
so newMarkerImage
can be created in line from markerImage
.
- (void)updateMyFlagsWitAlert:(BOOL)isAllowed{
for (AGSGraphic *graphic in weakSelf.flagOverlay.graphics) {
FlagModel *flagToUpdate = graphic.attributes[@"flag"];
UIImage *newMarkerImage =
[weakSelf markerImageForFlag:flagToUpdate withDetail:detail];
}
}
- (UIImage *)markerImageForFlag:(FlagModel *)flag withDetail:(BOOL)withDetail {
// This line crashes app
FlagMarkerView *markerView = [[[NSBundle mainBundle] loadNibNamed:@"FlagMarkerView" owner:self options:nil] objectAtIndex:0];
[markerView setFlag:flag];
UIImage *markerImage = [markerView imageWithDetail:withDetail];
[markerView layoutIfNeeded];
return markerImage;
}
Upvotes: 0
Views: 103
Reputation: 377
The following code-change solved my problem. I found the problem is not with markerImageForFlag
at all even though the Main Thread Checker highlighted the markerView
. I believe the weakSelf
reference put the task on the background thread, which was ok for awhile, but doesn't work anymore. All I had to do is put the dispatch_sync
, not dispatch_async
, on the call, newMarkerImage
. Thanks to @Rob and @Moshe! See below.
- (void)updateMyFlagsWitAlert:(BOOL)isAllowed{
for (AGSGraphic *graphic in weakSelf.flagOverlay.graphics) {
FlagModel *flagToUpdate = graphic.attributes[@"flag"];
__block UIImage *newMarkerImage = nil;
dispatch_sync(dispatch_get_main_queue(), ^{
newMarkerImage = [weakSelf markerImageForFlag:flagToUpdate withDetail:(scale < markerThreshold)];
});
}
}
Upvotes: 0
Reputation: 4003
You should probably redesign your code to be async, but the quickest way I could think of is something like:
__block FlagMarkerView *markerView;
dispatch_sync(dispatch_get_main_queue(), ^{
markerView = [[[NSBundle mainBundle] loadNibNamed:@"FlagMarkerView" owner:self options:nil] objectAtIndex:0];
[markerView setFlag:flag];
UIImage *markerImage = [markerView imageWithDetail:withDetail];
[markerView layoutIfNeeded];
});
return markerImage;
This will make your code run in the main thread, and wait for it to finish.
It is not a good design, you should probably design your code better and not rely on a return value.
Instead, your code could use a block as a parameter that will receive the result after it was processed in the main thread, instead of waiting for it using a semaphore.
But sometimes, if you just need something to work - this solution could be helpful.
Upvotes: 2