如何防止UIScrollView在激活iOS 13上下文菜单时放大一吨?

问题描述 投票:0回答:1

如果你有一个 UIScrollView 你可以放大图像,而你在滚动视图内的视图中添加一个iOS 13上下文菜单交互(例如:a UIImageView),当你执行该交互时,它会奇怪地瞬间放大图像,然后将其放大,以便显示上下文菜单,然后在退出该上下文菜单时,它将图像放大得很远。它似乎是要离开UIImageView的边界。

StackOverflow似乎不支持嵌入视频GIF,所以这里有一段Imgur上的视频,显示我的意思。https:/imgur.commAzWlJA

有什么办法可以防止这种行为吗?在 WKWebView (a UIScrollView 子类)为例,长按图片不会出现这种行为。

如果你想在一个简单的新Xcode项目中测试它,这里有一个简单的代码来展示它的示例。

import UIKit

class RootViewController: UIViewController, UIScrollViewDelegate, UIContextMenuInteractionDelegate {
    let scrollView = UIScrollView()
    let imageView = UIImageView(image: UIImage(named: "cat.jpg")!)

    override func viewDidLoad() {
        super.viewDidLoad()

        [view, scrollView].forEach { $0.backgroundColor = .black }

        scrollView.delegate = self
        scrollView.frame = view.bounds
        scrollView.addSubview(imageView)
        scrollView.contentSize = imageView.frame.size
        view.addSubview(scrollView)

        // Set zoom scale
        let scaleToFit = min(scrollView.bounds.width / imageView.bounds.width, scrollView.bounds.height / imageView.bounds.height)
        scrollView.maximumZoomScale = max(1.0, scaleToFit)
        scrollView.minimumZoomScale = scaleToFit < 1.0 ? scaleToFit : 1.0
        scrollView.zoomScale = scaleToFit

        // Add context menu support
        imageView.isUserInteractionEnabled = true
        imageView.addInteraction(UIContextMenuInteraction(delegate: self))
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        scrollView.frame = view.bounds
    }

    // MARK: - UIScrollView

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    // MARK: - Context Menus

    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        return UIContextMenuConfiguration(identifier: nil, previewProvider: { () -> UIViewController? in
            return nil
        }) { (suggestedElements) -> UIMenu? in
            var children: [UIAction] = []

            children.append(UIAction(title: "Upvote", image: UIImage(systemName: "arrow.up")) { (action) in
            })

            children.append(UIAction(title: "Downvote", image: UIImage(systemName: "arrow.down")) { (action) in
            })

            return UIMenu(title: "", image: nil, identifier: nil, options: [], children: children)
        }
    }
}

这里是 cat.jpg 如果你也喜欢的话。https:/imgur.comhTTZaw4。

ios uiscrollview ios13 uicontextmenuinteraction uicontextmenuconfiguration
1个回答
2
投票

我想我解决了。解决方案的要点是 将交互添加到图像视图本身,就像你直觉认为的那样,但将其添加到外部视图中,然后将上下文菜单预览聚焦到图像视图的矩形上,使用 UITargetPreview APIs。这样一来,你就一起避免了触碰图片视图的bug,而是去它的父视图,只需 "裁剪 "到子视图,这样就能让子视图保持快乐的状态了:)

这是我最后的代码。

import UIKit

class RootViewController: UIViewController, UIScrollViewDelegate, UIContextMenuInteractionDelegate {
    let wrapperView = UIView()
    let scrollView = UIScrollView()
    let imageView = UIImageView(image: UIImage(named: "cat.jpg")!)

    override func viewDidLoad() {
        super.viewDidLoad()

        wrapperView.frame = view.bounds
        view.addSubview(wrapperView)

        [view, wrapperView, scrollView].forEach { $0.backgroundColor = .black }

        scrollView.delegate = self
        scrollView.frame = view.bounds
        scrollView.addSubview(imageView)
        scrollView.contentSize = imageView.frame.size
        wrapperView.addSubview(scrollView)

        // Set zoom scale
        let scaleToFit = min(scrollView.bounds.width / imageView.bounds.width, scrollView.bounds.height / imageView.bounds.height)
        scrollView.maximumZoomScale = max(1.0, scaleToFit)
        scrollView.minimumZoomScale = scaleToFit < 1.0 ? scaleToFit : 1.0
        scrollView.zoomScale = scaleToFit

        // Add context menu support
        wrapperView.addInteraction(UIContextMenuInteraction(delegate: self))
    }

    // MARK: - UIScrollView

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    // MARK: - Context Menus

    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        scrollView.zoomScale = scrollView.minimumZoomScale

        return UIContextMenuConfiguration(identifier: nil, previewProvider: { () -> UIViewController? in
            return nil
        }) { (suggestedElements) -> UIMenu? in
            var children: [UIAction] = []

            children.append(UIAction(title: "Upvote", image: UIImage(systemName: "arrow.up")) { (action) in
            })

            children.append(UIAction(title: "Downvote", image: UIImage(systemName: "arrow.down")) { (action) in
            })

            return UIMenu(title: "", image: nil, identifier: nil, options: [], children: children)
        }
    }

    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, previewForHighlightingMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
        let parameters = UIPreviewParameters()

        let rect = imageView.convert(imageView.bounds, to: wrapperView)
        parameters.visiblePath = UIBezierPath(roundedRect: rect, cornerRadius: 13.0)

        return UITargetedPreview(view: wrapperView, parameters: parameters)
    }

    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, previewForDismissingMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
        let parameters = UIPreviewParameters()

        let rect = imageView.convert(imageView.bounds, to: wrapperView)
        parameters.visiblePath = UIBezierPath(roundedRect: rect, cornerRadius: 0.0)

        return UITargetedPreview(view: wrapperView, parameters: parameters)
    }
}

一些说明:

  • 不幸的是,当视图被放大时,这并不能很好地工作(没有我做的一个改变)。不管出于什么原因,iOS还是会试图搞乱滚动视图,这次把它放大,但没有渲染它周围的区域,导致不完整的图像视图周围有大片的白色区域。唏嘘不已。到此为止,我也算是完事了,你或许可以尝试在内部用一些 UIScrollView 子类,试图斥责iOS级别的变化,但是是的,我已经花了差不多的时间在这上面了,所以我只是将scrollView的zoomScale重置为完全放大,一旦它要求上下文菜单(注意你必须在这里做,在上下文菜单的 willPresent 上下文菜单API就太晚了)。) 它并不坏,完全解决了这个问题,只是重置用户的缩放级别有点烦人。但如果我收到支持邮件,我就把他们链接到这个帖子上。
  • 角落半径13.0与iOS默认的一致。唯一的问题是,这并不像iOS的那样从0到圆角半径的动画,它有点跳动,但几乎看不出来。我相信一定有办法解决这个问题,上下文菜单API的标题中提到了动画的一些功能,但是文档真的很缺乏,我不想花大量的时间去想办法。
  • 在这个例子中,我使用了 wrapperView 内的视图控制器视图。这可能是针对我的用例,在你的用例中可能没有必要。本质上,你可以把它附加到 scrollView 本身,但我的有一些自定义的镶边,让它始终在缺口iPhone内居中,关于安全区域镶边,如果我用滚动视图来做交互目标预览,就会把它跳来跳去,这看起来并不好。你也不想直接使用视图控制器的视图作为交互,因为它在做动画的时候会把它遮挡住,这样媒体查看器scroll视图的黑色背景就完全消失了,看起来不是很好。所以在顶层设置一个包装器视图就很好的防止了这两点。
© www.soinside.com 2019 - 2024. All rights reserved.