Подтвердить что ты не робот

Предварительный просмотр камеры UIImagePickerController - портрет в ландшафтном приложении

В моем приложении iPhone только для ландшафта я запускаю UIImagePickerController, чтобы делать снимок, но изображение, отображаемое с камеры в реальном времени, находится в портретной ориентации с пустым пространством вокруг него. Изображение поворачивается.

Как только кнопка камеры нажата, предварительный просмотр становится очень грязным, с большей частью предварительного просмотра за пределами экрана, и представления не выровнены правильно.

Apple признала, что это дефект, и работает над этим.

Мой вопрос: есть ли у кого-нибудь обходной путь (легальный или нелегальный), который позволил бы мне получить эту работу сейчас. Я бы не выпустил в App Store нелегальное исправление, но у меня было бы гораздо лучшее приложение для пользовательского тестирования - в настоящее время камера практически непригодна для использования в ландшафте.

Я приложу простой тестовый проект и изображения, если смогу.

Редактировать - просто чтобы уточнить, изображение, которое я получаю, правильно в альбомной ориентации. Я хочу, чтобы камера и предварительный просмотр выглядели правильно!


Camera

alt text

4b9b3361

Ответ 1

Ответ более смешной, чем вы думаете. У меня была такая же проблема, и я нашел решение на каком-то форуме. Передайте полученное изображение в метод, подобный этому:

// Code from: http://discussions.apple.com/thread.jspa?messageID=7949889
- (UIImage *)scaleAndRotateImage:(UIImage *)image {
  int kMaxResolution = 640; // Or whatever

    CGImageRef imgRef = image.CGImage;

    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);


    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    if (width > kMaxResolution || height > kMaxResolution) {
        CGFloat ratio = width/height;
        if (ratio > 1) {
            bounds.size.width = kMaxResolution;
            bounds.size.height = roundf(bounds.size.width / ratio);
        }
        else {
            bounds.size.height = kMaxResolution;
            bounds.size.width = roundf(bounds.size.height * ratio);
        }
    }

    CGFloat scaleRatio = bounds.size.width / width;
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
    CGFloat boundHeight;
    UIImageOrientation orient = image.imageOrientation;
    switch(orient) {

        case UIImageOrientationUp: //EXIF = 1
            transform = CGAffineTransformIdentity;
            break;

        case UIImageOrientationUpMirrored: //EXIF = 2
            transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            break;

        case UIImageOrientationDown: //EXIF = 3
            transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationDownMirrored: //EXIF = 4
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
            transform = CGAffineTransformScale(transform, 1.0, -1.0);
            break;

        case UIImageOrientationLeftMirrored: //EXIF = 5
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationLeft: //EXIF = 6
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationRightMirrored: //EXIF = 7
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeScale(-1.0, 1.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        case UIImageOrientationRight: //EXIF = 8
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];

    }

    UIGraphicsBeginImageContext(bounds.size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
        CGContextScaleCTM(context, -scaleRatio, scaleRatio);
        CGContextTranslateCTM(context, -height, 0);
    }
    else {
        CGContextScaleCTM(context, scaleRatio, -scaleRatio);
        CGContextTranslateCTM(context, 0, -height);
    }

    CGContextConcatCTM(context, transform);

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
    UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageCopy;
}

: (

Ответ 2

Я не думаю, что вам нужна дополнительная работа для работы с imageRotation или данными EXIF. Image drawInRect автоматически позаботится об этом.

Итак, вам нужно только получить этот размер или изображение и перерисовать его на новое изображение, этого было бы достаточно.

Ответ 3

Я не хочу поворачивать изображение после захвата; Я хочу, чтобы предварительный просмотр показывался правильно в ландшафтном режиме. Итак, в iOS 6 я разрешаю портретный режим на уровне приложения, но устанавливаю контроллер представления корневого приложения в класс MyNonrotatingNavigationController, определяемый следующим образом:

@implementation MyNonrotatingNavigationController

-(NSUInteger) supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

@end

Итак, все, что когда-либо отображается внутри этого контроллера, будет в альбомной ориентации (вы можете сделать это с помощью любого контроллера вида). Теперь, когда мне нужно отобразить подборщик изображений, я заменяю контроллер корневого окна окна приложения на общий, поддерживающий портретный режим. Чтобы предотвратить удаление старого контроллера корневого представления и его представлений, я поддерживаю указатели на них, пока я не буду готов вернуть их в окно приложения.

#define APP_DELEGATE ((MyAppDelegate*)[[UIApplication sharedApplication] delegate])
static UIViewController *pickerContainer = nil;
static UIViewController *oldRoot = nil;
static UIView *holdView = nil;

-(void) showPicker
{
    ...create UIImagePickerController...

    oldRoot = APP_DELEGATE.window.rootViewController;
    holdView = [[UIView alloc] initWithFrame:APP_DELEGATE.window.bounds];
    [holdView addSubview:oldRoot.view];

    pickerContainer = [UIViewController new];
    pickerContainer.view = [[UIView alloc] initWithFrame:APP_DELEGATE.window.bounds];
    APP_DELEGATE.window.rootViewController = pickerContainer;
    [pickerContainer presentViewController:picker animated:YES completion:NULL];
}

-(void) imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
    [pickerContainer dismissViewControllerAnimated:YES completion:^{
        dispatch_async( dispatch_get_main_queue(), ^{
            APP_DELEGATE.window.rootViewController = oldRoot;
            [APP_DELEGATE.window addSubview:oldRoot.view];
            pickerContainer = nil;
            oldRoot = nil;
            holdView = nil;
        });
    }];
}

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

Ответ 4

Я решил эту проблему, создав UIImagePickerController в полноэкранном режиме, что также рекомендует Apple для iPad.

Из UIImagePickerController документация:

На iPad, если вы укажете тип источника UIImagePickerControllerSourceTypeCamera, вы можете представить средство выбора изображений по умолчанию (полноэкранный) или с помощью popover. Однако Apple рекомендует, чтобы вы отображали интерфейс камеры только в полноэкранном режиме.

Ответ 5

Вам нужно будет настроить пользовательский вид (с панелью инструментов и названием) как cameraOverlayView, также вам нужно будет установить allowsEditing и showsCameraControls в NO поскольку это скроет стандартные элементы управления и предварительный просмотр. Это то, что я нашел по мере необходимости в приложении, которое я делаю (подумал, что мне нужно, чтобы сборщик был на портрете, но мне нужно, чтобы он применил некоторые изменения ui, если пользователь поворачивает свое устройство на пейзаж).
Если это необходимо, код может быть предоставлен.

PS В моем коде все еще есть ошибки, но я над этим работаю :)

Ответ 6

Эту проблему можно решить, установив для modalPresentationStyle значение overCurrentContext как overCurrentContext ниже:

 picker.modalPresentationStyle = .overCurrentContext

и представить свой подборщик изображений в Main Thread как,

DispatchQueue.main.async {

   self.present(picker, animated: true) {

   }
}

Почему это происходит?

Поскольку ImagePickerController работает только в портретном режиме, и когда вы пытаетесь представить средство выбора из ландшафтного видового контроллера, он пытается установить вашу строку состояния в портретном режиме. Итак, если вы установите для modalPresentationStyle значение overCurrentContext он не будет пытаться установить ориентацию. Это будет учитывать текущую ориентацию.

см. руководство Apple для ImagePickerController. Говорится,

Класс UIImagePickerController поддерживает только портретный режим. Этот класс предназначен для использования как есть и не поддерживает создание подклассов. Иерархия представления для этого класса является частной и не должна изменяться, за одним исключением. Вы можете назначить собственное представление свойству cameraOverlayView и использовать это представление для представления дополнительной информации или управления взаимодействиями между интерфейсом камеры и вашим кодом.

Ответ 7

Дайте мне знать, если это не рекомендуется повторно опубликовать лучший ответ выше, у меня есть быстрая версия 2019 для ответа Алекса Уэйна.

/**
 Scale and rotate a UIImage so that it is correctly oriented

 :param: image: The image to be rotated

 :returns: UIImage
 */
func scaleAndRotateImage(_ image: UIImage) -> UIImage {
    let kMaxResolution: CGFloat = 640
    let imgRef: CGImage = image.cgImage!
    let width: CGFloat = CGFloat(imgRef.width)
    let height: CGFloat = CGFloat(imgRef.height)
    var transform: CGAffineTransform = CGAffineTransform.identity
    var bounds: CGRect = CGRect(x: 0, y: 0, width: width, height: height)
    if width > kMaxResolution || height > kMaxResolution {
        let ratio: CGFloat = width / height
        if ratio > 1 {
            bounds.size.width = kMaxResolution
            bounds.size.height = (bounds.size.width / ratio)
        }
        else {
            bounds.size.height = kMaxResolution
            bounds.size.width = (bounds.size.height * ratio)
        }
    }
    let scaleRatio: CGFloat = bounds.size.width / width
    let imageSize: CGSize = CGSize(width: CGFloat(imgRef.width), height: CGFloat(imgRef.height))
    var boundHeight: CGFloat
    let orient: UIImage.Orientation = image.imageOrientation
    switch orient {
    case UIImageOrientation.up:
        transform = CGAffineTransform.identity
    case UIImageOrientation.upMirrored:
        transform = CGAffineTransform(translationX: imageSize.width, y: 0.0)
        transform = transform.scaledBy(x: -1.0, y: 1.0)
    case UIImageOrientation.down:
        transform = CGAffineTransform(translationX: imageSize.width, y: imageSize.height)
        transform = transform.rotated(by: CGFloat(Double.pi))
    case UIImageOrientation.downMirrored:
        transform = CGAffineTransform(translationX: 0.0, y: imageSize.height)
        transform = transform.scaledBy(x: 1.0, y: -1.0)
    case UIImageOrientation.leftMirrored:
        boundHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = boundHeight
        transform = CGAffineTransform(translationX: imageSize.height, y: imageSize.width)
        transform = transform.scaledBy(x: -1.0, y: 1.0)
        transform = transform.rotated(by: CGFloat(3.0 * .pi / 2.0))
    case UIImageOrientation.left:
        boundHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = boundHeight
        transform = CGAffineTransform(translationX: 0.0, y: imageSize.width)
        transform = transform.rotated(by: CGFloat(3.0 * .pi / 2.0))
    case UIImageOrientation.rightMirrored:
        boundHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = boundHeight
        transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
        transform = transform.rotated(by: CGFloat(.pi / 2.0))
    case UIImageOrientation.right:
        boundHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = boundHeight
        transform = CGAffineTransform(translationX: imageSize.height, y: 0.0)
        transform = transform.rotated(by: CGFloat(.pi / 2.0))
    }
    UIGraphicsBeginImageContext(bounds.size)
    let context: CGContext = UIGraphicsGetCurrentContext()!
    if orient == UIImage.Orientation.right || orient == UIImage.Orientation.left {
        context.scaleBy(x: -scaleRatio, y: scaleRatio)
        context.translateBy(x: -height, y: 0)
    }
    else {
        context.scaleBy(x: scaleRatio, y: -scaleRatio)
        context.translateBy(x: 0, y: -height)
    }
    context.concatenate(transform)
    UIGraphicsGetCurrentContext()?.draw(imgRef, in: CGRect(x: 0, y: 0, width: width, height: height))
    let imageCopy: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return imageCopy
}