ForceZoom:使用 3D Touch Peek 弹出图像细节视图

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

我在 iPhone 6s 上 的 3D Touch 实验 继续 ForceZoom ,这是一个扩展的 UIImageView ,它显示大图像上触摸点的 1:1 细节视图。

演示(上图)包含三个大图像, 森林 (1600 x 1200)、 药房 (4535 x 1823) 和 petronas (3264 x 4896)。对图像的初始触摸会显示触摸位置周围的预览框,然后深按会弹出该点全分辨率图像的方形预览。分辨率越高,预览框越小。

安装与实施

ForceZoom 包含两个需要复制到宿主应用程序项目中的文件:

要在应用程序中实现 ForceZoom 组件,请使用默认图像和视图控制器实例化并添加到视图中:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}

显示预览帧

由于弹出预览将是屏幕上可以容纳的最大正方形:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}

白色预览框是一个 CAShapeLayer,它的大小需要与图像在屏幕上的缩放比例相同。执行此操作的数学是在 touchesBegan 调用的 displayPreviewFrame() 方法中:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}

其中 imageScale 只是组件的宽度或高度除以图像的宽度或高度:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}

启动 Peek 预览

当调用 previewingContext(viewControllerForLocation) 以响应用户的深压时, ForceZoom 需要将触摸的规范化位置传递给预览组件。这是因为我使用弹出图像视图层的 contentsRect 来定位和裁剪全分辨率图像,而 contentsRect 使用标准化图像坐标。

在 previewingContext(viewControllerForLocation) 中有几个步骤可以做到这一点。首先,我将预览帧的大小计算为标准化值。这将用作触摸原点的偏移量以形成剪辑矩形的原点:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}


接下来,我计算组件边缘与其包含的图像边缘之间的距离:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}


然后,根据触摸点的位置和这两个新值,我可以创建剪辑矩形的标准化 x 原点:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}


我对 y 做同样的事情,并用这两个标准化值创建一个预览点:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}


...传递给我的 ForceZoomPreview


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}

窥视预览

预览组件现在几乎没有什么工作要做。它在其构造函数(上方)中传递了规范化原点,因此它需要做的就是使用这些值来设置图像视图的 contentsRect:


 class ViewController: UIViewController
    {
        var imageView: ForceZoomWidget!
    override func viewDidLoad()
    {
        super.viewDidLoad()

        imageView = ForceZoomWidget(image: UIImage(named: "forest.jpg")!,
            viewController: self)

        view.addSubview(imageView)
    }
}

源代码

一如既往,这个项目的完整源代码可以在 我的 GitHub 存储库 中找到。享受!