Статьи

ForceZoom: подробный всплывающий вид изображения с использованием 3D Touch Peek

Мои  эксперименты с 3D Touch  на iPhone 6s продолжаются с  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)
        }
    }

Отображение рамки предварительного просмотра

Так как во всплывающем превью будет самый большой квадрат, который может поместиться на экране:

    var peekPreviewSize: CGFloat
    {
        return min(UIScreen.mainScreen().bounds.size.width,
            UIScreen.mainScreen().bounds.size.height)
    }

Белое окно предварительного просмотра, которое является CAShapeLayer, должно быть такого размера в том же масштабе, что и изображение, масштабированное на экране. Математика для этого находится в методе displayPreviewFrame (), который вызывается touchesBegan:

    let previewFrameSize = peekPreviewSize * imageScale

Где imageScale — это просто ширина или высота компонента, деленная на ширину или высоту изображения:

   var imageScale: CGFloat
    {
        return min(bounds.size.width / image!.size.width, bounds.size.height / image!.size.height)

    }

Запуск предварительного просмотра

Когда previewingContext (viewControllerForLocation) вызывается в ответ на глубокое нажатие пользователя,  ForceZoom должен передать компоненту предварительного просмотра нормализованную позицию касания. Это связано с тем, что я использую объект contentRect всплывающего изображения для позиционирования и обрезки изображения с полным разрешением, а contentRect использует нормализованные координаты изображения.

Есть несколько шагов в previewingContext (viewControllerForLocation), чтобы сделать это. Прежде всего, я вычисляю размер рамки предварительного просмотра как нормализованное значение. Это будет использоваться как смещение от источника касания для формирования источника прямоугольника клипа:

    let offset = ((peekPreviewSize * imageScale) / (imageWidth * imageScale)) / 2

Затем я вычисляю расстояние между краем компонента и краем изображения, которое он содержит:

    let leftBorder = (bounds.width - (imageWidth * imageScale)) / 2

Затем, с расположением точки касания , и эти два новых значений, я могу создать нормализованное  х  происхождение клипа прямоугольника:

    let normalisedXPosition = ((location.x - leftBorder) / (imageWidth * imageScale)) - offset

Я делаю то же самое для  и с этими двумя нормализованными значениями создаю точку предварительного просмотра:

    let topBorder = (bounds.height - (imageHeight * imageScale)) / 2
    let normalisedYPosition = ((location.y - topBorder) / (imageHeight * imageScale)) - offset


    let normalisedPreviewPoint = CGPoint(x: normalisedXPosition, y: normalisedYPosition)

… который передается в мой  ForceZoomPreview :

    let peek = ForceZoomPreview(normalisedPreviewPoint: normalisedPreviewPoint, image: image!)

The Peek Preview

У компонента предварительного просмотра теперь очень мало работы. Он передал нормализованный источник в своем конструкторе (выше), поэтому все, что ему нужно сделать, это использовать эти значения для установки contentRect представления изображения:

    imageView.layer.contentsRect = CGRect(
        x: max(min(normalisedPreviewPoint.x, 1), 0),
        y: max(min(normalisedPreviewPoint.y, 1), 0),
        width: view.frame.width / image.size.width,

        height: view.frame.height / image.size.height)

Исходный код

Как всегда, полный исходный код этого проекта доступен в  моем репозитории GitHub здесь . Наслаждайтесь!