繁体   English   中英

UIImagePicker 允许编辑卡在中心

[英]UIImagePicker allowsEditing stuck in center

我有一个 UIImagePicker 非常适合一种 UIImagePickerControllerSourceTypePhotoLibrary,但是当我使用 UIImagePickerControllerSourceTypeCamera 时,编辑框无法从图像的中心移动。 因此,如果图像的高度大于宽度,则用户无法将编辑框移动到图像的顶部正方形。

有谁知道为什么会这样? 仅当源来自相机而不是库时才会发生。

编辑:一些代码!!!

if (actionSheet.tag == 2) {
    if (buttonIndex == 0) { // Camera
        // Check for camera
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == YES) {
            // Create image picker controller
            UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

            // Set source to the camera
            imagePicker.sourceType =  UIImagePickerControllerSourceTypeCamera;
            imagePicker.allowsEditing = YES;

            // Delegate is self
            imagePicker.delegate = self;

            // Show image picker
            [self presentViewController:imagePicker 
                               animated:YES 
                             completion:^(void) {
                             }];
        }
    }
    else if (buttonIndex == 1) { // Photo Library
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary] == YES) {
            // Create image picker controller
            UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

            // Set source to the camera
            imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
            imagePicker.allowsEditing = YES;

            // Delegate is self
            imagePicker.delegate = self;

            // Show image picker
            [self presentViewController:imagePicker 
                               animated:YES 


                          completion:^(void) {
                                 }];
            }
}

如您所见,我将它们显示为完全相同,但相机编辑的行为与照片库编辑不同。

看起来这种行为只是iOS 6中的一个错误......基本上你不能移动编辑框,除非你放大一点,否则它总是弹回中间。 希望他们能尽快解决这个问题。

谢谢 扩展程序有效。 除了我在viewDidLayoutSubviews中添加了方法调用,这样我就不必每次想要打开图像选择器时都调用它。

这是完整的扩展

extension UIImagePickerController {
    open override var childForStatusBarHidden: UIViewController? {
        return nil
    }

    open override var prefersStatusBarHidden: Bool {
        return true
    }
    
    open override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        fixCannotMoveEditingBox()
    }
    
    func fixCannotMoveEditingBox() {
            if let cropView = cropView,
               let scrollView = scrollView,
               scrollView.contentOffset.y == 0 {
                
                var top: CGFloat = 0.0
                if #available(iOS 11.0, *) {
                    top = cropView.frame.minY + self.view.safeAreaInsets.top
                } else {
                    // Fallback on earlier versions
                    top = cropView.frame.minY
                }
                let bottom = scrollView.frame.height - cropView.frame.height - top
                scrollView.contentInset = UIEdgeInsets(top: top, left: 0, bottom: bottom, right: 0)
                
                var offset: CGFloat = 0
                if scrollView.contentSize.height > scrollView.contentSize.width {
                    offset = 0.5 * (scrollView.contentSize.height - scrollView.contentSize.width)
                }
                scrollView.contentOffset = CGPoint(x: 0, y: -top + offset)
            }
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
                self?.fixCannotMoveEditingBox()
            }
        }
        
        var cropView: UIView? {
            return findCropView(from: self.view)
        }
        
        var scrollView: UIScrollView? {
            return findScrollView(from: self.view)
        }
        
        func findCropView(from view: UIView) -> UIView? {
            let width = UIScreen.main.bounds.width
            let size = view.bounds.size
            if width == size.height, width == size.height {
                return view
            }
            for view in view.subviews {
                if let cropView = findCropView(from: view) {
                    return cropView
                }
            }
            return nil
        }
        
        func findScrollView(from view: UIView) -> UIScrollView? {
            if let scrollView = view as? UIScrollView {
                return scrollView
            }
            for view in view.subviews {
                if let scrollView = findScrollView(from: view) {
                    return scrollView
                }
            }
            return nil
        }
}

重置contentInsetscrollview

extension UIImagePickerController {
    func fixCannotMoveEditingBox() {
        if let cropView = cropView,
           let scrollView = scrollView,
           scrollView.contentOffset.y == 0 {
            
            let top = cropView.frame.minY + self.view.safeAreaInsets.top
            let bottom = scrollView.frame.height - cropView.frame.height - top
            scrollView.contentInset = UIEdgeInsets(top: top, left: 0, bottom: bottom, right: 0)
            
            var offset: CGFloat = 0
            if scrollView.contentSize.height > scrollView.contentSize.width {
                offset = 0.5 * (scrollView.contentSize.height - scrollView.contentSize.width)
            }
            scrollView.contentOffset = CGPoint(x: 0, y: -top + offset)
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
            self?.fixCannotMoveEditingBox()
        }
    }
    
    var cropView: UIView? {
        return findCropView(from: self.view)
    }
    
    var scrollView: UIScrollView? {
        return findScrollView(from: self.view)
    }
    
    func findCropView(from view: UIView) -> UIView? {
        let width = UIScreen.main.bounds.width
        let size = view.bounds.size
        if width == size.height, width == size.height {
            return view
        }
        for view in view.subviews {
            if let cropView = findCropView(from: view) {
                return cropView
            }
        }
        return nil
    }
    
    func findScrollView(from view: UIView) -> UIScrollView? {
        if let scrollView = view as? UIScrollView {
            return scrollView
        }
        for view in view.subviews {
            if let scrollView = findScrollView(from: view) {
                return scrollView
            }
        }
        return nil
    }
}

然后调用它

imagePickercontroller.fixCannotMoveEditingBox()

这是 Image Picker Controller 的默认行为,您无法更改它。 唯一的其他选择是创建自己的裁剪实用程序。 查看下面的链接以获取示例:

https://github.com/ardalahmet/SSPhotoCropperViewController

我知道,这不是一个好的解决方案,但它确实有效。

我在iOS8+iPhone5、iOS9+iPhone6sPlus、iOS10+iPhone6、iOS10+iPhone6sPlus上进行了测试。

注意PLImageScrollViewPLCropOverlayCropViewUNDOCUMENTED类。

- (void)showImagePickerControllerWithSourceType:(UIImagePickerControllerSourceType)sourceType {
    UIImagePickerController *imagePickerController = [UIImagePickerController new];
    imagePickerController.sourceType = sourceType;
    imagePickerController.mediaTypes = @[(NSString *)kUTTypeImage];
    imagePickerController.allowsEditing = YES;
    imagePickerController.delegate = self;
    [self presentViewController:imagePickerController animated:YES completion:^{
        [self fxxxImagePickerController:imagePickerController];
    }];
}

- (void)fxxxImagePickerController:(UIImagePickerController *)imagePickerController {
    if (!imagePickerController
        || !imagePickerController.allowsEditing
        || imagePickerController.sourceType != UIImagePickerControllerSourceTypeCamera) {
        return;
    }

    // !!!: UNDOCUMENTED CLASS
    Class ScrollViewClass = NSClassFromString(@"PLImageScrollView");
    Class CropViewClass = NSClassFromString(@"PLCropOverlayCropView");

    [imagePickerController.view eachSubview:^BOOL(UIView *subview, NSInteger depth) {
        if ([subview isKindOfClass:CropViewClass]) {
            // 0. crop rect position
            subview.frame = subview.superview.bounds;
        }
        else if ([subview isKindOfClass:[UIScrollView class]]
            && [subview isKindOfClass:ScrollViewClass]) {
            BOOL isNewImageScrollView = !self->_imageScrollView;
            self->_imageScrollView = (UIScrollView *)subview;
            // 1. enable scrolling
            CGSize size = self->_imageScrollView.frame.size;
            CGFloat inset = ABS(size.width - size.height) / 2;
            self->_imageScrollView.contentInset = UIEdgeInsetsMake(inset, 0, inset, 0);
            // 2. centering image by default
            if (isNewImageScrollView) {
                CGSize contentSize = self->_imageScrollView.contentSize;
                if (contentSize.height > contentSize.width) {
                    CGFloat offset = round((contentSize.height - contentSize.width) / 2 - inset);
                    self->_imageScrollView.contentOffset = CGPointMake(self->_imageScrollView.contentOffset.x, offset);
                }
            }
        }
        return YES;
    }];

    // prevent re-layout, maybe not necessary
    @weakify(self, imagePickerController);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        @strongify(self, imagePickerController);
        [self fxxxImagePickerController:imagePickerController];
    });
}

编辑eachSubview:方法遍历所有子视图树。

如果您在 info.plist 中将“查看基于控制器的状态栏外观”设置为 NO 并将状态栏外观设置为浅色,请使用

 UIApplication.shared.statusBarStyle = .lightContent

或使用任何其他方法,然后只需在呈现图像选择器之前将样式设置为 .default 即可。 例如:

imagePicker.allowsEditing = true
imagePicker.sourceType = .photoLibrary
UIApplication.shared.statusBarStyle = .default
present(imagePicker, animated: true, completion: nil)

根据您的需要将源类型更改为photoLibrary 或相机,并在您的didFinishPickingMediaWithInfo的完成块中将以下内容添加到完成块中。

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    //let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage

    var pickedImage : UIImage?
    if let img = info[UIImagePickerControllerEditedImage] as? UIImage
    {
        pickedImage = img

    }
    else if let img = info[UIImagePickerControllerOriginalImage] as? UIImage
    {
        pickedImage = img
    }
    dismiss(animated: true, completion: {
        UIApplication.shared.statusBarStyle         = .lightContent
    })}

显然这是相同的解决方法。希望这会有所帮助。

这是我最终使用的一个扩展,它在缺口和非缺口设备上都可以正常工作。 在 iOS 15 上完美运行!

extension UIImagePickerController {
    open override var childForStatusBarHidden: UIViewController? {
        return nil
    }

    open override var prefersStatusBarHidden: Bool {
        return true
    }
    
    open override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        fixCannotMoveEditingBox()
    }
    
    private func fixCannotMoveEditingBox() {
        if let cropView = cropView, let scrollView = scrollView, scrollView.contentOffset.y == 0 {
            let top: CGFloat = cropView.frame.minY + self.view.frame.minY
            let bottom = scrollView.frame.height - cropView.frame.height - top
            scrollView.contentInset = UIEdgeInsets(top: top, left: 0, bottom: bottom, right: 0)
            
            var offset: CGFloat = 0
            if scrollView.contentSize.height > scrollView.contentSize.width {
                offset = 0.5 * (scrollView.contentSize.height - scrollView.contentSize.width)
            }
            scrollView.contentOffset = CGPoint(x: 0, y: -top + offset)
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
            self?.fixCannotMoveEditingBox()
        }
    }
    
    private var cropView: UIView? {
        return findCropView(from: self.view)
    }
    
    private var scrollView: UIScrollView? {
        return findScrollView(from: self.view)
    }
    
    private func findCropView(from view: UIView) -> UIView? {
        let width = UIScreen.main.bounds.width
        let size = view.bounds.size
        if width == size.height, width == size.height {
            return view
        }
        for view in view.subviews {
            if let cropView = findCropView(from: view) {
                return cropView
            }
        }
        return nil
    }
    
    private func findScrollView(from view: UIView) -> UIScrollView? {
        if let scrollView = view as? UIScrollView {
            return scrollView
        }
        for view in view.subviews {
            if let scrollView = findScrollView(from: view) {
                return scrollView
            }
        }
        return nil
    }
}

ps:与其他类似答案的唯一变化是:

  • 放弃对旧 iOS 版本(如 iOS 11)的支持;
  • 计算 contentInset 顶部属性的方式有点不同;

解决它的一种解决方法是在 info.plist 中添加一个条目,并将“基于控制器的状态栏外观”设置为 NO

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM