iOS. Приемы программирования Нахавандипур Вандад
Далее нужно обработать различные сообщения, получаемые от делегата видеоредактора в контроллере вида:
— (void)videoEditorController:(UIVideoEditorController *)editor
didSaveEditedVideoToPath:(NSString *)editedVideoPath{
NSLog(@"The video editor finished saving video");
NSLog(@"The edited video path is at = %@", editedVideoPath);
[editor dismissModalViewControllerAnimated: YES];
}
— (void)videoEditorController:(UIVideoEditorController *)editor
didFailWithError:(NSError *)error{
NSLog(@"Video editor error occurred = %@", error);
[editor dismissModalViewControllerAnimated: YES];
}
— (void)videoEditorControllerDidCancel:(UIVideoEditorController *)editor{
NSLog(@"The video editor was cancelled");
[editor dismissModalViewControllerAnimated: YES];
}
Когда вид загрузится, мы должны будем отобразить для пользователя вид для выбора видео. Видео он может выбрать из библиотеки, а потом мы предоставим возможность его обрабатывать:
— (BOOL) cameraSupportsMedia:(NSString *)paramMediaType
sourceType:(UIImagePickerControllerSourceType)paramSourceType{
__block BOOL result = NO;
if ([paramMediaType length] == 0){
NSLog(@"Media type is empty.");
return NO;
}
NSArray *availableMediaTypes =
[UIImagePickerController
availableMediaTypesForSourceType: paramSourceType];
[availableMediaTypes enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop) {
NSString *mediaType = (NSString *)obj;
if ([mediaType isEqualToString: paramMediaType]){
result = YES;
*stop= YES;
}
}];
return result;
}
— (BOOL) canUserPickVideosFromPhotoLibrary{
return [self cameraSupportsMedia:(__bridge NSString *)kUTTypeMovie
sourceType: UIImagePickerControllerSourceTypePhotoLibrary];
}
— (BOOL) isPhotoLibraryAvailable{
return [UIImagePickerController
isSourceTypeAvailable:
UIImagePickerControllerSourceTypePhotoLibrary];
}
— (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear: animated];
static BOOL beenHereBefore = NO;
if (beenHereBefore){
/* Отображаем элемент для выбора даты только после того, как вызывается
метод viewDidAppear:, что происходит при каждом отображении вида
нашего контроллера вида */
return;
} else {
beenHereBefore = YES;
self.assetsLibrary = [[ALAssetsLibrary alloc] init];
}
if ([self isPhotoLibraryAvailable] &&
[self canUserPickVideosFromPhotoLibrary]){
UIImagePickerController *iPicker =
[[UIImagePickerController alloc] init];
/* Задаем тип источника для библиотеки фотографий. */
iPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
/* Требуется, чтобы пользователь мог выбирать видеоролики
из библиотеки. */
NSArray *mediaTypes = [[NSArray alloc] initWithObjects:
(__bridge NSString *)kUTTypeMovie, nil];
iPicker.mediaTypes = mediaTypes;
/* Задаем делегат для текущего контроллера вида. */
iPicker.delegate = self;
/* Отображаем окно для выбора изображений. */
[self.navigationController presentModalViewController: iPicker
animated: YES];
}
}
Далее нам необходимо узнать, когда пользователь сделает выбор (то есть выберет видеоролик). Обработаем различные сообщения делегата, обслуживающего инструмент для выбора изображений:
— (void) iPickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info{
NSLog(@"Picker returned successfully.");
NSString *mediaType = [info objectForKey:
UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]){
self.videoURLToEdit =
[info objectForKey: UIImagePickerControllerMediaURL];
}
[picker dismissModalViewControllerAnimated: YES];
/* Сначала убедимся, что редактор видео способен обрабатывать видео,
путь к которому в папке документов мы указали. */
if ([UIVideoEditorController canEditVideoAtPath: videoPath]){
/* Инстанцируем редактор видео. */
UIVideoEditorController *videoEditor =
[[UIVideoEditorController alloc] init];
/* Становимся делегатом видеоредактора. */
videoEditor.delegate = self;
/* Убеждаемся, что задали путь к видеоролику. */
videoEditor.videoPath = videoPath;
/* А теперь отображаем видеоредактор. */
[self.navigationController presentModalViewController: videoEditor
animated: YES];
self.videoURLToEdit = nil;
} else {
NSLog(@"Cannot edit the video at this path");
}
}
}];
}
— (void) iPickerControllerDidCancel:(UIImagePickerController *)picker{
NSLog(@"Picker was cancelled");
self.videoURLToEdit = nil;
[picker dismissViewControllerAnimated: YES completion: nil];
}
В данном примере пользователь может выбрать любое видео из библиотеки фотографий. Как только он это сделает, мы отобразим контроллер видеоредактора, указав путь к видеоролику. Этот путь передает нам инструмент для выбора видео в методе делегата.
Делегат контроллера видеоредактора получает важные сообщения о состоянии этого видеоредактора. Объект делегата должен соответствовать протоколам UIVideoEditorControllerDelegate и UINavigationControllerDelegate. В данном примере мы решили, что делегатом видеоредактора должен стать контроллер вида. Как только редактирование будет закончено, объект делегата получит от контроллера видеоредактора метод делегата videoEditorController: didSaveEditedVideoToPath:. Путь к отредактированному видео будет передан в параметре didSaveEditedVideoToPath.
Прежде чем попытаться отобразить для пользователя интерфейс видеоредактора, нужно вызывать метод класса canEditVideoAtPath:, относящийся к классу UIVideoEditorController. Мы это делаем, чтобы убедиться, что тот путь, который вы пытаетесь редактировать, доступен для обработки в контроллере. Если возвращаемое значение этого метода класса равно YES, можно переходить к конфигурированию и отображению интерфейса редактора видео. Если нет — идем другим путем. Возможно, потребуется отобразить для пользователя предупреждение.
См. также
Разделы 13.6 и 13.7.
Глава 14. Многозадачность
14.0. Введение
Многозадачность — это способ работы, обеспечивающий фоновое выполнение. Иначе говоря, приложение может работать как обычно — выполнять задачи, порождать новые потоки, слушать уведомления, реагировать на события, — но просто ничего не отображать на экране и не взаимодействовать с пользователем каким-либо образом. Когда пользователь нажимает на устройстве кнопку Home (Домой) — в более ранних версиях iPhone и iPad эта кнопка завершала работу приложения, — программа просто переходит в фоновый режим.
Когда ваше приложение переходит в фоновый режим (например, при нажатии кнопки Home (Домой)), а затем возвращается на передний план (когда пользователь вновь выбирает это приложение), система начинает посылать различные сообщения. Ожидается, что эти сообщения будут получены объектом, который мы назначаем делегатом нашего приложения. Например, когда приложение переходит в фоновый режим, делегат приложения получит метод applicationDidEnterBackground:. Аналогично, когда пользователь вновь вернет приложение в приоритетный режим, то есть возобновит с ним работу, делегат приложения получит делегатное сообщение applicationWillEnterForeground:.
Кроме этих сообщений делегатов iOS также посылает работающему приложению уведомления, когда переводит эту программу в фоновый режим или возвращает обратно в приоритетный режим. При переходе приложения в фоновый режим посылается уведомление UIApplicationDidEnterBackgroundNotification, а при переходе из фонового режима в приоритетный — уведомление UIApplicationWillEnterForegroundNotification. Для регистрации этих уведомлений можно использовать центр уведомлений, задаваемый по умолчанию.
14.1. Обнаружение доступности многозадачности
Постановка задачи
Необходимо выяснить, поддерживается ли многозадачность на том устройстве с iOS, на котором работает ваше приложение.
Решение
Вызовите метод экземпляра isMultitaskingSupported, относящийся к классу UIDevice:
— (BOOL) isMultitaskingSupported{
BOOL result = NO;
if ([[UIDevice currentDevice]
respondsToSelector:@selector(isMultitaskingSupported)]){
result = [[UIDevice currentDevice] isMultitaskingSupported];
}
return result;
}
— (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
if ([self isMultitaskingSupported]){
NSLog(@"Multitasking is supported.");
} else {
NSLog(@"Multitasking is not supported.");
}
self.window = [[UIWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Обсуждение
В зависимости от того, на работу в какой версии iOS рассчитано ваше приложение, его можно запускать и выполнять на различных устройствах, где установлены разные версии iOS. Например, вы можете разрабатывать приложение в последней версии iOS SDK, но в качестве целевой (наиболее ранняя версия iOS, в которой сможет работать ваше приложение) задать более низкую версию. В этом случае приложение сможет работать на устройстве с более ранней версией операционной системы, но, возможно, это устройство не будет поддерживать многозадачность. Золотое правило, действующее как в программировании, так и в жизни вообще (не подумайте, что я берусь философствовать), таково: если вы пытаетесь гадать, то рано или поздно ошибетесь. Поэтому не пытайтесь угадать, на каких устройствах в данное время работает ваше приложение. Ведь если iOS-разработчик указывает в Xcode минимальную поддерживаемую версию iOS, он тем самым автоматически ограничивает свою пользовательскую аудиторию. Наша цель — добиться, чтобы обладатели любых устройств с iOS, существующих в настоящее время, могли пользоваться нашим приложением. Поэтому если вы хотите задействовать в приложении для iOS новейшие возможности многозадачности, обязательно проверяйте, доступна ли многозадачность на данном устройстве. Если многозадачность недоступна, необходимо гарантировать, что приложение корректно отреагирует на это, например выберет альтернативный режим выполнения.
14.2. Выполнение долгосрочной задачи в фоновом режиме
Постановка задачи
Требуется «занять» немного времени у iOS и выполнить долгосрочную задачу, пока приложение находится в фоновом режиме.
Решение
Воспользуйтесь методом экземпляра beginBackgroundTaskWithExpirationHandler:, относящимся к классу UIApplication. После того как задача будет решена, вызовите метод экземпляра endBackgroundTask:, относящийся к классу UIApplication.
Обсуждение
Когда приложение переходит в фоновый режим, работа его основного потока приостанавливается. Потоки, которые вы создаете в своем приложении с помощью метода класса detachNewThreadSelector: toTarget: withObject:, относящегося к классу NSThread, также приостанавливаются. Если вы пытаетесь решить долгосрочную задачу за то время, пока приложение находится в фоновом режиме, необходимо вызвать метод экземпляра beginBackgroundTaskWithExpirationHandler:, относящийся к классу UIApplication, чтобы «занять» немного времени у системы iOS. Свойство backgroundTimeRemaining класса UIApplication содержит количество секунд, которые требуются приложению для завершения той или иной задачи. Если приложение не успеет завершить долгосрочную задачу до того, как истечет отведенный временной промежуток, iOS завершит приложение. За каждым вызовом метода beginBackgroundTaskWithExpirationHandler: должен следовать соответствующий вызов метода endBackgroundTask: (другой метод экземпляра UIApplication). Иными словами, если вы просите у iOS дополнительное время на завершение задачи, то должны и сообщить iOS, когда закончите эту задачу. Когда это будет сделано и окажется, что больше нет задач, которые следовало бы выполнить в фоновом режиме, ваше приложение перейдет в фоновый режим целиком и все его потоки будут приостановлены.
Когда приложение работает на переднем плане, свойство backgroundTimeRemaining класса UIApplication равно константе DBL_MAX, означающей максимальную величину, которая может содержаться в значении типа double (число с двойной точностью). В данном случае целочисленное значение, равное такому двойному вещественному числу, обычно составляет –1. После того как у iOS запрашивается дополнительное время перед полной приостановкой приложения, в этом свойстве указывается количество секунд, требуемое программе для завершения задачи (или всех текущих задач).
Можно вызывать в приложении метод beginBackgroundTaskWithExpirationHandler: столько раз, сколько потребуется. Но важно учитывать, что, когда iOS возвращает в этом методе вашей программе метку (токен) или идентификатор задачи, вы должны вызвать метод endBackgroundTask:, и сделать это сразу же, как программа закончит выполнять задачу. Если не сделать этого, операционная система iOS может завершить приложение.
Если приложение работает в фоновом режиме, то не предполагается, что оно останется полностью функциональным и сможет обрабатывать «тяжелые» данные. Действительно, такие приложения рассчитаны лишь на завершение долгосрочных задач.
В качестве примера можно привести приложение, направившее вызов к API веб-службы и еще не получившее с сервера ответ от этого API. На время ожидания приложение будет отправлено в фоновый режим, и приложение сможет запросить дополнительное время для получения ответа с сервера. Как только ответ будет получен, приложение должно сохранить свое состояние, а потом отметить факт завершения задачи, вызвав метод экземпляра endBackgroundTask:, относящийся к классу UIApplication.
Рассмотрим пример. Начнем с того, что определим в делегате приложения свойство типа UIBackgroundTaskIdentifier. Кроме того, определим таймер типа NSTimer, который будет использоваться при ежесекундном выводе сообщений на консоль, после того как приложение уйдет в фоновый режим:
#import «AppDelegate.h»
@interface AppDelegate ()
@property (nonatomic, unsafe_unretained)
UIBackgroundTaskIdentifier backgroundTaskIdentifier;
@property (nonatomic, strong) NSTimer *myTimer;
@end
@implementation AppDelegate
<# Остаток вашего кода находится здесь #>
Теперь создадим таймер и назначим время перехода приложения в фоновый режим:
— (BOOL) isMultitaskingSupported{
BOOL result = NO;
if ([[UIDevice currentDevice]
respondsToSelector:@selector(isMultitaskingSupported)]){
result = [[UIDevice currentDevice] isMultitaskingSupported];
}
return result;
}
— (void) timerMethod:(NSTimer *)paramSender{
NSTimeInterval backgroundTimeRemaining =
[[UIApplication sharedApplication] backgroundTimeRemaining];
if (backgroundTimeRemaining == DBL_MAX){
NSLog(@"Background Time Remaining = Undetermined");
} else {
NSLog(@"Background Time Remaining = %.02f Seconds",
backgroundTimeRemaining);
}
}
— (void)applicationDidEnterBackground:(UIApplication *)application{
if ([self isMultitaskingSupported] == NO){
return;
}
self.myTimer =
[NSTimer scheduledTimerWithTimeInterval:1.0f
target: self
selector:@selector(timerMethod:)
userInfo: nil
repeats: YES];
self.backgroundTaskIdentifier =