【问题标题】:iPad iOS7 - UIImagePickerController in UIPopoverController has wrong preview imageiPad iOS7 - UIPopoverController 中的 UIImagePickerController 有错误的预览图像
【发布时间】:2013-09-30 06:17:23
【问题描述】:

我在 UIPopoverController 中使用 UIImagePickerController,它与 iOS6 完美配合。在 iOS 7 中,为了捕捉图像而显示的“预览”图像会旋转,但如果我拍照,它会被正确保存。

这就是我获得选择器的方式:

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

并将其添加到弹出框控制器:

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 上按钮位置的计算,以在正确的位置显示弹出框:

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

我认为问题不在于那里,因为我已经尝试了几种组合。

我也尝试在全屏模式下捕捉图像,但该应用只允许使用横向模式。如果图像是在纵向模式下拍摄的并且模态视图被关闭,则应用程序也会保持纵向模式。如果模式视图被关闭,我找不到阻止 UIImagePickerController 切换到纵向模式或强制应用返回横向模式的方法。

更新

我已经从here那里得到了答案,并且更进一步。

我在创建选择器之后和显示弹出框之前转换视图:

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;
        }
    }
}

剩下的问题: 正如我在开始时所写的那样,当照片被拍摄时,它被正确地显示为接受或拒绝它。现在也进行了改造。不知何故,我需要知道何时拍摄图像并将其转换回来。

而且,这确实是一个令人讨厌的 hack,并且可能不适用于下一个 iOS 更新。有人知道如何以更清洁的方式实现它吗?

更新 2

这太讨厌了,我找到了一个更干净的解决方案,可以解决我的问题,但不是关于弹出框控制器中图像选择器的初始问题的答案,Apple 不推荐,但允许这样做。

我现在已经像这样子类化 UIImagePickerController:

@implementation QPImagePickerController

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

- (BOOL)shouldAutorotate {
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscape;
}

@end

我正在全屏使用图像选择器,而不是在弹出窗口中。目前在 iOS7 中测试。

【问题讨论】:

  • 我面临着完全相同的问题。我不敢相信这不是一个更相关的问题——它怎么不破坏更多的应用程序?
  • 一模一样的问题,下次更新不会解决吗?
  • 在我看来,同样的问题正在影响使用弹出式摄像头的 ios7 facebook 应用程序。
  • 在 iOS7 iPad 环境中只有应用程序我得到'UIApplicationInvalidInterfaceOrientation',原因:'支持的方向与应用程序没有共同的方向,并且 shouldAutorotate 正在返回 YES'

标签: ios uiimagepickercontroller ios7 uipopovercontroller uiinterfaceorientation


【解决方案1】:

UIImagePickerController 有一个名为 cameraViewTransform 的属性。对此应用 CGAffineTransform 将转换预览图像,但不会转换捕获的图像,因此将被正确捕获。我有你描述的同样的问题,我通过创建我的相机控制器并将它放在一个弹出窗口中解决了它(对于 iOS7),如下所示:

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】:

    我找到了另一种解决方案,它可以根据 Journeyman 提供的答案处理在 UIIMagePickerView 呈现时设备旋转的情况。它还处理视图从 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;
            }
        }
    
    }
    

    这里的重要情况是设备方向变为纵向的情况。在这种情况下,需要重置叠加层的视图。在图像选择器视图加载后调用 fixCameraOrientation 选择器也很重要,以防设备旋转:

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

    【讨论】:

      【解决方案3】:

      我在我的应用中遇到了类似的情况。然而,预览在 iOS7 中正确旋转,而不是在 iOS8 中。此代码假定您有多个方向。

      首先要继承UIImagePickerController

      从顶部开始,将 #import &lt;AVFoundation/AVFoundation.h&gt; 添加到您的 .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 &amp;&amp; self.sourceType == UIImagePickerControllerSourceTypeCamera)。如果是,设置起始方向self.startingOrientation = [UIApplication sharedApplication].statusBarOrientation;

      -statusBarOrientationDidChange: 中添加与上述相同的条件,但这次如果为真,我们将更新相机变换。首先,我们根据初始旋转获得偏移旋转。当您以纵向以外的方向输入 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: 中,我们简单地循环超级视图链,如果其中一个类的名称中有CameraView,则返回true。

      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 - 不在弹出窗口中显示。相反,呈现在模态视图控制器中,就像在 iPhone 上一样。 (至少从 iOS 7 开始)

      【讨论】:

      • 正是我需要的。似乎在 iOS 8 iPad 环境中,popover 最适合库,但模态视图演示更适合相机。
      猜你喜欢
      • 1970-01-01
      • 2013-04-03
      • 2017-08-20
      • 1970-01-01
      • 2013-11-22
      • 1970-01-01
      • 1970-01-01
      • 2012-12-17
      • 1970-01-01
      相关资源
      最近更新 更多