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

IPad iOS7 - UIImagePickerController в UIPopoverController имеет неправильное изображение предварительного просмотра

Я использую UIImagePickerController в UIPopoverController, который отлично работает с iOS6. С iOS 7 изображение "предварительного просмотра", которое показано для захвата изображения, поворачивается, но если я делаю снимок, он сохраняется правильно.

Вот как я получаю свой сборщик:

UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePicker.mediaTypes = [NSArray arrayWithObjects:
                              (NSString *) kUTTypeImage,
                              nil];
imagePicker.allowsEditing = NO;

И добавьте его в контроллер popover:

self.imagePickerPopOver = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
    [self.imagePickerPopOver presentPopoverFromRect:CGRectMake(aPosViewA.x, cameraButton_y, 100.0, 30.0) inView:self.detailViewController.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

Это расчеты для позиции кнопки в UIScrollView, чтобы отобразить popover в правильном положении:

presentPopoverFromRect:CGRectMake(aPosViewA.x, cameraButton_y, 100.0, 30.0)

Я не думаю, что проблема кроется там, поскольку я опробовал несколько комбинаций.

Я также попытался захватить изображение в полноэкранном режиме, но приложению разрешено использовать только ландшафтный режим. Если изображение выполняется в портретном режиме, а модальное представление отклонено, приложение также остается в портретном режиме. Я не мог найти способ не допустить перехода UIImagePickerController в портретный режим или принудительное возвращение приложения в ландшафтный режим, если модальное представление было отклонено.

ОБНОВЛЕНИЕ

Я принял ответ здесь и сделал еще один шаг.

Я преобразую представление после создания сборщика и перед показом popover:

switch ([UIApplication sharedApplication].statusBarOrientation) {
        case UIInterfaceOrientationLandscapeLeft:
            self.imagePicker.view.transform = CGAffineTransformMakeRotation(M_PI/2);
            break;
        case UIInterfaceOrientationLandscapeRight:
            self.imagePicker.view.transform = CGAffineTransformMakeRotation(-M_PI/2);
            break;
        default:
            break;
    }

который работает до тех пор, пока я не обернусь iPad. Для этого я регистрирую для события с измененной ориентацией:

[[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(orientationChanged:)  name:UIDeviceOrientationDidChangeNotification  object:nil];

и измените представление выбора:

- (void)orientationChanged:(NSNotification *)notification{

    if (self.imagePicker) {
        switch ([UIApplication sharedApplication].statusBarOrientation) {
            case UIInterfaceOrientationLandscapeLeft:
                self.imagePicker.view.transform = CGAffineTransformMakeRotation(M_PI/2);
                break;
            case UIInterfaceOrientationLandscapeRight:
                self.imagePicker.view.transform = CGAffineTransformMakeRotation(-M_PI/2);
                break;
            default:
                break;
        }
    }
}

ОСТАВШАЯСЯ ПРОБЛЕМА: Как я писал в начале, когда фотография была сделана, было правильно показано, чтобы принять или отклонить ее. Это также трансформируется. Как-то мне нужно знать, когда будет снято изображение и преобразовать его.

И, это действительно неприятный взлом и, вероятно, не будет работать со следующим обновлением iOS. Кто-нибудь знает, как реализовать это более чистым способом?

ОБНОВЛЕНИЕ 2

Это было слишком противно, я нашел более чистое решение, которое решает мою проблему, но не является ответом на начальный вопрос относительно imagepicker в контроллере popover, который не рекомендуется Apple, но разрешен.

Теперь я подклассифицировал UIImagePickerController следующим образом:

@implementation QPImagePickerController

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}

- (BOOL)shouldAutorotate {
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscape;
}

@end

и я использую imagepicker в полноэкранном режиме вместо этого в popover. Протестировано до сих пор в iOS7.

4b9b3361

Ответ 1

UIImagePickerController имеет свойство, называемое cameraViewTransform. Применение CGAffineTransform к этому преобразует изображение предварительного просмотра, но не преобразует захваченное изображение, которое поэтому будет правильно захвачено. У меня такая же проблема, что и вы ее описали, и я решил ее (для iOS7), создав свой контроллер камеры и поместив его в popover следующим образом:

UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];

[imagePickerController setDelegate:self];

imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;

CGFloat scaleFactor=1.3f;

switch ([UIApplication sharedApplication].statusBarOrientation) {

        case UIInterfaceOrientationLandscapeLeft:

            imagePickerController.cameraViewTransform = CGAffineTransformScale(CGAffineTransformMakeRotation(M_PI * 90 / 180.0), scaleFactor, scaleFactor);

            break;

        case UIInterfaceOrientationLandscapeRight:

            imagePickerController.cameraViewTransform = CGAffineTransformScale(CGAffineTransformMakeRotation(M_PI * -90 / 180.0), scaleFactor, scaleFactor);

            break;

        case UIInterfaceOrientationPortraitUpsideDown:

            imagePickerController.cameraViewTransform = CGAffineTransformMakeRotation(M_PI * 180 / 180.0);

            break;

            default:
                break;
        }

 __popoverController = [[UIPopoverController alloc] initWithContentViewController:imagePickerController];

[__popoverController presentPopoverFromRect:presentationRect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

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

Ответ 2

Я нашел другое решение, которое обрабатывает случай, когда устройство вращается, а UIIMagePickerView представляет на основе ответа, предоставленного Journeyman. Он также обрабатывает случай, когда представление поворачивается из UIOrientationLandscapeRight/UIOrientationLandscapeLeft обратно в UIOrientationPortrait.

Я создал подкласс UIImagePickerController:

#import <UIKit/UIKit.h>

@interface PMImagePickerController : UIImagePickerController

@end

Затем зарегистрировано для получения уведомлений, если меняется ориентация устройства:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fixCameraOrientation:) name:UIDeviceOrientationDidChangeNotification object:nil];

Селектор fixCameraOrientation содержит код Journeyman с одним дополнительным случаем, завернутый в проверку, чтобы убедиться, что sourceType - это камера:

- (void)fixCameraOrientation:(NSNotification*)notification
{
    if (self.sourceType == UIImagePickerControllerSourceTypeCamera) {
        CGFloat scaleFactor=1.3f;

        switch ([UIApplication sharedApplication].statusBarOrientation) {
            case UIInterfaceOrientationLandscapeLeft:
                self.cameraViewTransform = CGAffineTransformScale(CGAffineTransformMakeRotation(M_PI * 90 / 180.0), scaleFactor, scaleFactor);
                break;


            case UIInterfaceOrientationLandscapeRight:
                self.cameraViewTransform = CGAffineTransformScale(CGAffineTransformMakeRotation(M_PI * -90 / 180.0), scaleFactor, scaleFactor);
                break;

            case UIInterfaceOrientationPortraitUpsideDown:
                self.cameraViewTransform = CGAffineTransformMakeRotation(M_PI * 180 / 180.0);
                break;

            case UIInterfaceOrientationPortrait:
                self.cameraViewTransform = CGAffineTransformIdentity;
                break;

            default:
                break;
        }
    }

}

Важным здесь является случай, когда ориентация устройства переходит к портрету. В этом случае представление оверлей должно быть reset. Также важно, чтобы селектор fixCameraOrientation вызывался после загрузки изображения выбора изображения, в случае поворота устройства:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self fixCameraOrientation:nil];
}

Ответ 3

У меня была аналогичная ситуация в моем приложении. Однако предварительный просмотр корректно вращается в iOS7, а не в iOS8. Этот код предполагает, что у вас более одной ориентации.

Первое, что нужно сделать для подкласса UIImagePickerController.

Начиная с вершины, добавьте #import <AVFoundation/AVFoundation.h> в ваш .m файл.

Также добавьте свойство, чтобы сохранить начальную ориентацию @property (nonatomic) UIInterfaceOrientation startingOrientation;, а другое - для условия удаления обрезки @property (nonatomic) BOOL didAttemptToRemoveCropping;.

Собирались слушать несколько уведомлений. UIApplicationDidChangeStatusBarOrientationNotification - это, очевидно, прослушивание вращения устройства. AVCaptureSessionDidStartRunningNotification называется правильным, когда камера начинает захватывать.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationDidChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionDidStart:) name:AVCaptureSessionDidStartRunningNotification object:nil];

В -captureSessionDidStart: добавьте условие, чтобы проверить, что представление действительно находится на экране, и чтобы убедиться, что камера должна отображаться if (self.view.window && self.sourceType == UIImagePickerControllerSourceTypeCamera). Если это так, установите начальную ориентацию self.startingOrientation = [UIApplication sharedApplication].statusBarOrientation;.

В -statusBarOrientationDidChange: добавьте те же условия, что и выше, но на этот раз, если true, мы обновим преобразование камеры. Сначала мы получаем вращение смещения на основе начального вращения. Это необходимо, когда вы вводите UIImagePickerController в ориентациях, кроме портрета.

CGFloat startingRotation = ({
    CGFloat rotation;

    switch (self.startingOrientation) {
        case UIInterfaceOrientationPortraitUpsideDown:
            rotation = M_PI;
            break;
        case UIInterfaceOrientationLandscapeLeft:
            rotation = -M_PI_2;
            break;
        case UIInterfaceOrientationLandscapeRight:
            rotation = M_PI_2;
            break;
        default:
            rotation = 0.0f;
            break;
    }

    rotation;
});

Далее мы обновим преобразование камеры с текущим вращением.

self.cameraViewTransform = CGAffineTransformMakeRotation(({
    CGFloat angle;

    switch ([UIApplication sharedApplication].statusBarOrientation) {
        case UIInterfaceOrientationPortraitUpsideDown:
            angle = startingRotation + M_PI;
            break;
        case UIInterfaceOrientationLandscapeLeft:
            angle = startingRotation + M_PI_2;
            break;
        case UIInterfaceOrientationLandscapeRight:
            angle = startingRotation + -M_PI_2;
            break;
        default:
            angle = startingRotation;
            break;
    }

    angle;
}));

И, наконец, мы попытаемся удалить черные полосы, представленные в 90-градусной ориентации от начальной ориентации. (Это может быть только проблема iOS8.) Чуть более подробно, если я войду в режим UIImagePickerController в портретном режиме, а затем поверните на пейзаж, в верхней и нижней частях предварительного просмотра будут черные полосы. Решением для этого является не масштабирование, а скорее удаление обрезки супервизора. Нам нужно только сделать эту попытку один раз, поэтому сначала проверьте, вызвал ли мы этот код. Также убедитесь, что мы вызываем этот код только при повороте. Если его вызвать в начальной ориентации, он не будет работать сразу.

if (!self.didAttemptToRemoveCropping && self.startingOrientation != [UIApplication sharedApplication].statusBarOrientation) {
    self.didAttemptToRemoveCropping = YES;

    [self findClippedSubviewInView:self.view];
}

Наконец, в коде для -findClippedSubviewInView: мы просматриваем все подзоны для поиска с помощью .clipsToBounds = YES. Если это правда, мы делаем еще одно условие для проверки правильности одного из своих предков.

for (UIView* subview in view.subviews) {
    if (subview.clipsToBounds) {
        if ([self hasAncestorCameraView:subview]) {
            subview.clipsToBounds = NO;
            break;
        }
    }

    [self findClippedSubviewInView:subview];
}

В -hasAncestorCameraView: мы просто зацикливаем цепочку супервизора и возвращаем true, если один из классов имеет CameraView в имени.

if (view == self.view) {
    return NO;
}

NSString* className = NSStringFromClass([view class]);

if ([className rangeOfString:@"CameraView"].location != NSNotFound) {
    return YES;

} else {
    return [self hasAncestorCameraView:view.superview];
}

Вот разбивка кода, вот и все вместе.

#import <AVFoundation/AVFoundation.h>
#import "GImagePickerController.h"

@interface GImagePickerController ()
@property (nonatomic) UIInterfaceOrientation startingOrientation;
@property (nonatomic) BOOL didAttemptToRemoveCropping;
@end

@implementation GImagePickerController

- (instancetype)init {
    self = [super init];
    if (self) {

        if ([[[UIDevice currentDevice] systemVersion] intValue] >= 8) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationDidChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureSessionDidStart:) name:AVCaptureSessionDidStartRunningNotification object:nil];
        }

    }
    return self;
}

- (void)dealloc {
    if ([[[UIDevice currentDevice] systemVersion] intValue] >= 8) {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
}


#pragma mark - Capture Session

- (void)captureSessionDidStart:(NSNotification *)notification {
    if (self.view.window && self.sourceType == UIImagePickerControllerSourceTypeCamera) {
        [self updateStartingOrientation];
    }
}


#pragma mark - Orientation

- (void)updateStartingOrientation {
    self.startingOrientation = [UIApplication sharedApplication].statusBarOrientation;
    [self updateCameraTransform];
}

- (void)updateCameraTransform {
    CGFloat startingRotation = ({
        CGFloat rotation;

        switch (self.startingOrientation) {
            case UIInterfaceOrientationPortraitUpsideDown:
                rotation = M_PI;
                break;
            case UIInterfaceOrientationLandscapeLeft:
                rotation = -M_PI_2;
                break;
            case UIInterfaceOrientationLandscapeRight:
                rotation = M_PI_2;
                break;
            default:
                rotation = 0.0f;
                break;
        }

        rotation;
    });

    self.cameraViewTransform = CGAffineTransformMakeRotation(({
        CGFloat angle;

        switch ([UIApplication sharedApplication].statusBarOrientation) {
            case UIInterfaceOrientationPortraitUpsideDown:
                angle = startingRotation + M_PI;
                break;
            case UIInterfaceOrientationLandscapeLeft:
                angle = startingRotation + M_PI_2;
                break;
            case UIInterfaceOrientationLandscapeRight:
                angle = startingRotation + -M_PI_2;
                break;
            default:
                angle = startingRotation;
                break;
        }

        angle;
    }));

    if (!self.didAttemptToRemoveCropping && self.startingOrientation != [UIApplication sharedApplication].statusBarOrientation) {
        self.didAttemptToRemoveCropping = YES;

        [self findClippedSubviewInView:self.view];
    }
}

- (void)statusBarOrientationDidChange:(NSNotification *)notification {
    if (self.view.window && self.sourceType == UIImagePickerControllerSourceTypeCamera) {
        [self updateCameraTransform];
    }
}


#pragma mark - Remove Clip To Bounds

- (BOOL)hasAncestorCameraView:(UIView *)view {
    if (view == self.view) {
        return NO;
    }

    NSString* className = NSStringFromClass([view class]);

    if ([className rangeOfString:@"CameraView"].location != NSNotFound) {
        return YES;

    } else {
        return [self hasAncestorCameraView:view.superview];
    }
}

- (void)findClippedSubviewInView:(UIView *)view {
    for (UIView* subview in view.subviews) {
        if (subview.clipsToBounds) {
            if ([self hasAncestorCameraView:subview]) {
                subview.clipsToBounds = NO;
                break;
            }
        }

        [self findClippedSubviewInView:subview];
    }
}

@end

Ответ 4

iPad с камерой - не отображается в popover. Вместо этого присутствуйте в модульном контроллере просмотра, как и на iPhone. (по крайней мере, начиная с iOS 7)