1

У меня есть приложение с кнопкой Share. Я хочу настроить общий контент, основанный на типе активности. Например, сообщения могут получить изображение и текст, тогда как AirDrop просто получит файл.Как я могу изменить элементы, используемые UIActivityViewController в этом сценарии?

У меня на самом деле это прекрасно работает, и код, который я использую, отлично работает в каждой версии iOS через iOS 10. Но я понял, что возвращаю ноль, где я не должен, поэтому я Я пытаюсь понять, как это исправить.

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

JUNActivityProvider *fileProvider = [[JUNActivityProvider alloc] initWithPlaceholderItem:[NSObject new]]; 
fileProvider.objectID = objectID; 
fileProvider.fileURL = fileURL; 

JUNActivityProvider *textProvider = [[JUNActivityProvider alloc] initWithPlaceholderItem:[NSString new]]; 
textProvider.objectID = objectID; 

... 

UIActivityViewController *activityController = [[UIActivityViewController alloc] 
    initWithActivityItems:@[fileProvider,imageProvider,textProvider,urlProvider,printFormatter] 
    applicationActivities:nil]; 

Тогда в JUNActivityProvider, у меня есть item метод, который настраивает возвращаемое значение на основе activityType:

- (id)item { 

    if (self.fileURL) { 

     if ([self.activityType isEqualToString:UIActivityTypeAirDrop]) { 

      // Create the file 
      return url; 

     } 

    } else if ([self.placeholderItem isKindOfClass:[UIImage class]]) { 

     if ([self.activityType isEqualToString:UIActivityTypeAirDrop] == NO && 
      [self.activityType isEqualToString:UIActivityTypeMail] == NO && 
      [self.activityType isEqualToString:UIActivityTypePrint] == NO) { 

      // Create the image 
      return image; 

     } 

    } else if ([self.placeholderItem isKindOfClass:[NSString class]]) { 

     if ([self.activityType isEqualToString:UIActivityTypeMail]) { 

      return @"example one"; 

     } else if ([self.activityType isEqualToString:UIActivityTypeMessage] || 
      [self.activityType isEqualToString:UIActivityTypeCopyToPasteboard]) { 

      return @"example two"; 

     } 

    } 

    return nil; 

} 

Это возвращение return nil в конце является проблемой. Он отлично работает и делает именно то, что я хочу - когда это ничто, что элемент не используется. Письменная документация не говорит, что она должна возвращать значение, но заголовочный файл:

- (nonnull id)item; // вызывается на вторичном потоке, когда пользователь выбирает активность. вы должны подклассифицировать и вернуть значение, отличное от нуля.

Я не хочу рисковать сбоем, возвращая нуль, когда ожидается ненулевое значение, поэтому мне нужно исправить это. Насколько я могу судить, мой единственный вариант - прекратить использование UIActivityItemProvider и вместо этого реализовать протокол UIActivityItemSource самостоятельно. Этот протокол включает в себя метод activityViewController:itemForActivityType:, в котором четко говорится, что вы можешь возврат ноля есть:

Может быть ноль, если несколько элементов были зарегистрированы для одного вида деятельности, так долго, как один из элементов возвращает фактическое значение ,

Perfect. Но вот проблема: activityViewController:itemForActivityType: вызывается в основном потоке, что вызывает проблемы с одним из моих элементов в частности. Вот краткое изложение того, что происходит:

  1. Мне нужно вызвать некоторые методы, которые выполняются асинхронно. Чтобы справиться с этим, я попытался использовать семафор отправки. Это препятствует возврату метода, пока у меня не будет возможности установить возвращаемое значение.
  2. С activityViewController:itemForActivityType: вызывается в основном потоке, который блокируется во время работы.
  3. Мне нужно нарисовать UIView в изображение. Если я попытаюсь выполнить эту работу в основном потоке, ничего не произойдет до тех пор, пока семафор не истечет. Но если я не делаю этого в основном потоке, он падает.

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

+0

Вы пытались вернуть '[NSNull null]' из вашей предыдущей попытки (UIActivityItemProvider)? Это возвращаемое значение «non-nil» в том смысле, что оно является объектом, но часто используется в API Cocoa для обозначения нулевого/пустого результата (и у меня был успех с этим в других местах с помощью UIActivityItemSource. – tgaul

+0

@tgaul : Использование '[NSNull null]' "работает" в том, что оно удовлетворяет требованию 'nonnull' и не * кажется * вызывать какие-либо проблемы, но это делает меня очень нервным - я не могу понять, что он делает с этим Я немного склонен просто придерживаться 'nil' только потому, что это не вызвало каких-либо заметных проблем в течение многих лет. – robotspacer

+0

Я подал сообщение об ошибке, предполагающее, что свойство' item' должно быть равно null: http://www.openradar.me/radar?id=4563896690016256 или rdar: // 28394697 – robotspacer

ответ

3

После подачи заявки enhancement request я как раз собирался сдаваться и соглашаться либо на возвращение nil, либо [NSNull null]. Но потом я понял, что есть абсолютно решение этой проблемы.

В то время как UIActivityItemProvider включает в себя набор собственных функций, он все еще очень реализует протокол UIActivityItemSource. Я знал это. Я не считал, что это означает, что я могу просто переопределить activityViewController:itemForActivityType: и вернуть nilтам, когда это уместно.

Так последняя строка моего item метода теперь выглядит следующим образом:

return self.placeholderItem; 

Вы также можете вернуться [NSNull null] здесь, или на самом деле какой-либо объект. Я выбрал placeholderItem, потому что он кажется немного более безопасным - по крайней мере, я знаю, что он возвращает объект ожидаемого типа, если что-либо изменится.

Тогда все, что нужно сделать, это добавить свою собственную реализацию activityViewController:itemForActivityType: (где мы которые разрешено вернуться ноль):

- (nullable id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(UIActivityType)activityType { 
    id item = [super activityViewController:activityViewController itemForActivityType:activityType]; 
    if ([item isEqual:self.placeholderItem]) return nil; 
    return item; 
} 

Просто позвоните супер, чтобы получить деталь, вернуть ноль, если это что-то вам не хотите включать или возвращать элемент, если он есть. Обратите внимание, что если ваш placeholderItem может быть когда-либо равным тому, что вы на самом деле сделать хотите поделиться, вам нужно будет немного изменить эту реализацию, но одна и та же базовая концепция должна работать.

 Смежные вопросы

  • Нет связанных вопросов^_^