Создать пользовательский UIView из подкласса в основной теме iOS - PullRequest
0 голосов
/ 25 марта 2020

У меня есть следующий код для метода run, который возвращает UIImage, созданный из UIView, созданного в отдельном файле класса UIView. FlagMarkerView является отдельным подклассом UIView и упоминается здесь. Проблема в том, что строка FlagMarker *markerView... выдает следующую ошибку средства проверки основного потока и приводит к сбою приложения.

UIView.init (кодер :) должен использоваться из основного потока.

Код, используемый для работы как есть, но больше не работает, поскольку я обновил проект до цели iOS 11.

Я попытался обернуть вызов FlagMarkerView в dispatch_async(dispatch_get_main_queue(), ^{});, но это не сработает, потому что оно не принимается возвращением UIImage в методе. Я также попытался использовать метод -(void) вместо возврата UIImage, но это связано со сложностью моего проекта.

Есть ли способ, которым я могу создать newMarkerImage в - (void)updateMyFlagsWitAlert: и использовать dispatch_async(dispatch_get_main_queue(), ^{ для FlagMarkerView, чтобы newMarkerImage можно было создать в строке от 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;
}

enter image description here

Ответы [ 2 ]

1 голос
/ 25 марта 2020

Вам, вероятно, следует изменить код так, чтобы он был асинхронным c, но самый быстрый способ, о котором я могу подумать, это что-то вроде:

    __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;

Это заставит ваш код работать в главном потоке и ждать для его завершения sh.
Это не очень хороший дизайн, вы, вероятно, должны разработать свой код лучше и не полагаться на возвращаемое значение.
Вместо этого, ваш код может использовать блок в качестве параметра, который будет получать результат после того, как он был обработан в главном потоке, вместо того, чтобы ждать его с помощью семафора.
Но иногда, если вам просто нужно что-то работать - это решение может быть полезным.

0 голосов
/ 26 марта 2020

Следующее изменение кода решило мою проблему. Я обнаружил, что проблема не в markerImageForFlag, даже в том случае, если средство проверки основных потоков выделило markerView. Я полагаю, что ссылка weakSelf помещает задачу в фоновый поток, который некоторое время был в порядке, но больше не работает. Все, что мне нужно было сделать, это поставить на вызов dispatch_sync, а не dispatch_async, newMarkerImage. Спасибо @Rob и @Moshe! См. ниже.

- (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)];
        });

    }

}

...