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

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

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 54w+ 字,讲解图 2476+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 1900+ 小伙伴加入学习 ,欢迎点击围观

我在 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 存储库 中找到。享受!