Вандад Нахавандипур - iOS. Приемы программирования
Теперь напишем простое приложение, которое будет получать данные о первом изображении, найденном в библиотеке ресурсов, создавать UIImageView с этим изображением, а потом добавлять это изображение в вид того контроллера вида, который отображается в настоящий момент. На данном примере мы научимся считывать содержимое ресурса, используя его представление.
Когда контроллер вида отображает свой вид, мы инициализируем объект библиотеки ресурсов и начнем перечисление ресурсов в этой библиотеке, пока не найдем первую фотографию. На данном этапе будем использовать представление этого ресурса (фотографии), чтобы отобразить фотографию в виде с изображением:
— (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear: animated];
static BOOL beenHereBefore = NO;
if (beenHereBefore){
/* Отображаем элемент для выбора даты только после того, как вызывается
метод viewDidAppear:, что происходит при каждом отображении вида
нашего контроллера вида */
return;
} else {
beenHereBefore = YES;
}
self.assetsLibrary = [[ALAssetsLibrary alloc] init];
dispatch_queue_t dispatchQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(dispatchQueue, ^(void) {
[self.assetsLibrary
enumerateGroupsWithTypes: ALAssetsGroupAll
usingBlock: ^(ALAssetsGroup *group, BOOL *stop) {
[group enumerateAssetsUsingBlock: ^(ALAsset *result,
NSUInteger index,
BOOL *stop) {
__block BOOL foundThePhoto = NO;
if (foundThePhoto){
*stop = YES;
}
/* Получаем тип ресурса. */
NSString *assetType = [result
valueForProperty: ALAssetPropertyType];
if ([assetType isEqualToString: ALAssetTypePhoto]){
NSLog(@"This is a photo asset");
foundThePhoto = YES;
*stop = YES;
/* Получаем объект представления ресурса. */
ALAssetRepresentation *assetRepresentation =
[result defaultRepresentation];
/* Нам требуются данные о масштабе и ориентации, чтобы можно
было создать правильно расположенное и вымеренное изображение
UIImage из нашего объекта представления. */
CGFloat imageScale = [assetRepresentation scale];
UIImageOrientation imageOrientation =
(UIImageOrientation)[assetRepresentation orientation];
dispatch_async(dispatch_get_main_queue(), ^(void) {
CGImageRef imageReference =
[assetRepresentation fullResolutionImage];
/* Сейчас создаем изображение. */
UIImage *image =
[[UIImage alloc] initWithCGImage: imageReference
scale: imageScale
orientation: imageOrientation];
if (image!= nil){
self.imageView = [[UIImageView alloc]
initWithFrame: self.view.bounds];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.image = image;
[self.view addSubview: self.imageView];
} else {
NSLog(@"Failed to create the image.");
}
});
}
}];
}
failureBlock: ^(NSError *error) {
NSLog(@"Failed to enumerate the asset groups.");
}];
});
}
Мы перечисляем группы и каждый ресурс в группе. Потом находим первый ресурс-фотографию и получаем его представление. С помощью этого представления создаем UIImage, а уже из UIImage делаем UIImageView для демонстрации изображения в виде. Ничего сложного, правда?
Работа с видеофайлами строится немного иначе, поскольку в классе ALAssetRepresentation нет каких-либо методов, способных возвращать объект, заключающий в себе видеофайл. Поэтому нам потребуется считать содержимое видеоресурса в буфер и, возможно, сохранить его в каталоге Documents, где впоследствии к этому документу будет проще получить доступ. Разумеется, конкретные требования зависят от определенного приложения, но в приведенном далее примере кода мы пойдем дальше — найдем первый видеофайл в библиотеке ресурсов и сохраним его в каталоге Documents в приложении под названием Temp.MOV:
— (NSString *) documentFolderPath{
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSURL *url = [fileManager URLForDirectory: NSDocumentDirectory
inDomain: NSUserDomainMask
appropriateForURL: nil
create: NO
error: nil]
return url.path;
}
— (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear: animated];
static BOOL beenHereBefore = NO;
if (beenHereBefore){
/* Отображаем элемент для выбора даты только после того, как вызывается
метод viewDidAppear:, что происходит при каждом отображении вида
нашего контроллера вида */
return;
} else {
beenHereBefore = YES;
}
self.assetsLibrary = [[ALAssetsLibrary alloc] init];
dispatch_queue_t dispatchQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(dispatchQueue, ^(void) {
[self.assetsLibrary
enumerateGroupsWithTypes: ALAssetsGroupAll
usingBlock: ^(ALAssetsGroup *group, BOOL *stop) {
__block BOOL foundTheVideo = NO;
[group enumerateAssetsUsingBlock: ^(ALAsset *result,
NSUInteger index,
BOOL *stop) {
/* Получаем тип ресурса. */
NSString *assetType = [result
valueForProperty: ALAssetPropertyType];
if ([assetType isEqualToString: ALAssetTypeVideo]){
NSLog(@"This is a video asset");
foundTheVideo = YES;
*stop = YES;
/* Получаем объект представления ресурса. */
ALAssetRepresentation *assetRepresentation =
[result defaultRepresentation];
const NSUInteger BufferSize = 1024;
uint8_t buffer[BufferSize];
NSUInteger bytesRead = 0;
long long currentOffset = 0;
NSError *readingError = nil;
/* Создаем путь, по которому должно быть сохранено видео. */
NSString *videoPath = [documentsFolder
stringByAppendingPathComponent:@"Temp.MOV"];
NSFileManager *fileManager = [[NSFileManager alloc] init];
/* Создаем файл, если он еще не существует. */
if ([fileManager fileExistsAtPath: videoPath] == NO){
[fileManager createFileAtPath: videoPath
contents: nil
attributes: nil];
}
/* Этот дескриптор файла мы будем использовать для записи
медийных ресурсов на диск. */
NSFileHandle *fileHandle = [NSFileHandle
fileHandleForWritingAtPath: videoPath];
do{
/* Считываем столько байтов, сколько можем поместить в буфер. */
bytesRead = [assetRepresentation getBytes:(uint8_t *)&buffer
fromOffset: currentOffset
length: BufferSize
error:&readingError];
/* Если ничего считать не можем, то выходим из этого цикла. */
if (bytesRead == 0){
break;
}
/* Данные о смещении буфера должны быть актуальными. */
currentOffset += bytesRead;
/* Помещаем буфер в объект NSData. */
NSData *readData = [[NSData alloc]
initWithBytes:(const void *)buffer
length: bytesRead];
/* И записываем данные в файл. */
[fileHandle writeData: readData];
} while (bytesRead > 0);
NSLog(@"Finished reading and storing the
video in the documents folder");
}
}];
if (foundTheVideo){
*stop = YES;
}
}
failureBlock: ^(NSError *error) {
NSLog(@"Failed to enumerate the asset groups.");
}];
});
}
Вот что происходит в данном коде.
1. Мы получаем стандартное представление первого видеоресурса, который находим в библиотеке ресурсов.
2. Создаем файл Temp.MOV в каталоге Documents нашего приложения для сохранения содержимого видеоресурса.
3. Создаем цикл, работающий до тех пор, пока в представлении ресурса еще остаются данные, которые необходимо считать. Метод экземпляра getBytes: fromOffset: length: error:, относящийся к объекту представления ресурса, считывает столько байтов, сколько может поместиться в буфер, и проделывает это столько раз, сколько необходимо, пока мы не достигнем конца данных представления.
4. После считывания данных в буфер мы заключаем их в объект типа NSData, используя для этого метод инициализации initWithBytes: length: класса NSData. Затем записываем эти данные в файл, созданный ранее, с помощью метода экземпляра writeData:, относящегося к классу NSFileHandle.
13.8. Редактирование видео на устройстве с операционной системой iOS
Постановка задачи
Требуется, чтобы пользователь, просматривающий видео, мог редактировать видео в этом же приложении.
Решение
Воспользуйтесь классом UIVideoEditorController. В приведенном здесь примере используем данный класс вместе с контроллером для выбора изображений. Сначала мы предлагаем пользователю выбрать снимок из его библиотеки фотографий. После того как выбор будет сделан, отображаем экземпляр контроллера видеоредактора, где пользователь может обрабатывать выбранное видео.