Масштабируем изображение в UITableView, iOS

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Сегодня во многих приложениях в AppStore есть поведение, что масштабирует картинки во время скролла в заголовке таблицы. В этой статье я покажу вам один из простейших способов реализации этого поведения.

Несмотря на то, что сейчас бОльшая часть новых статей по верстке относится к SwiftUI, я всё же решил рассмотреть данный подход с ипользованием Storyboard, так как еще очень многие проекты используют UIKit и полный переход на SwiftUI наверное будет тогда же, когда все перепишут свои проекты с Objective-C на Swift :)

Давайте для начала выполним предварительные действия:

  1. Создадим новый проект, в качестве верстки указываем опцию Storyboard.
  2. В самом сториборде располагаем UITableView.
  3. Положим пустой UIView внутрь UITableView Header. Данная view будет служить нам контейнером. Для нашего же удобства — давайте переименуем в навигаторе это новую view в Container View.
  4. Добавим UIImageView в наш вновь созданный контейнер и в attributes inspector’е для данной image view выставляем content mode — Aspect Fill.

Итоговая структура нашего контроллера должна выглядеть следующим образом:

Приблизетльная структура контроллера

Давайте теперь начнем с констрейнтов для нашей table view. Выставим ограничения для всех 4-ех сторон в 0. Так же нужно убедится, что констрейнты выставлены относительно Safe Area, за исключением top констрейнта.

Констрейнты для UITableView

Теперь нам необходимо выставить констрейнты для нашей image view. Здесь практически аналогично — устанавливаем ограничения для левой, нижней и правой сторон, а так же констрейнт высоты, который должен быть равен высоте его контейнера (imageView.height = containerView.height x multiplier = 1.0)

Констрейнты для UIImageView

Если вы вдруг заблудились или запутались, вот краткое видео того, как настриваются констрейнты:

Инициализация констрейнтов

Переходим теперь к коду. Вначале нужно создать outlet’ы в нашем основном UIViewController’е:

  1. Первый аутлет — сама UITableView.
  2. Второй аутлет — констрейнт высоты картинки.

Для того, чтобы добавить поведение скейлинга для нашей картинки во время скролла, мы должны назначит делегата для UITableView.

Вот так выглядит код контроллера:

Промежуточное состояние контроллера

Основная работа будет проделана в методе scrollViewDidScroll. Здесь необходимо менять высоту картинки, чтобы достичь эффекта скейлинга.

Первое, что нам необходимо — это получить значение offset’a у scollView по оси y. Если выводить в консоль значение этого поля, то можно заметить, что оно отрицательное для тех случаев, когда мы скроллим таблицу сверху->вниз (и наоборот). Для нашего же удобства — давайте сделаем это значение положительным.

Значения по оси y

Второе, нам необходимо получить значение высоты хедера таблицы. Здесь всё просто, мы можем получить это значение из самой UITableView.

let headerContainerViewHeight = tableView.tableHeaderView?.frame.height ?? 0

Теперь, когда у нас есть эти два значения — мы можем модифицировать наш констрейнт высоты. Нижний предел нашей высоты может быть высота самого header’a (в моём случае это −260), а верхний предел в данном случае никак не лимитрован.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
  let yOffset = -scrollView.contentOffset.y
  let headerContainerViewHeight = tableView.tableHeaderView?.frame.height ?? 0
  spaceImageHeightConstraint.constant = max(yOffset, -headerContainerViewHeight)
}

Запускаем наше приложение и попробуем проскроллить нашу таблицу:

Отлично! Это как раз то, что нам нужно! Во многих проектах можно заметить что такого же эффекта достигают с помощью аффинных преобразований, но как мы видим существуют способы и попроще :)

👍НравитсяПонравилось13
В избранноеВ избранном3
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Отличное и простое решение для UITableView.
Часто подобное надо сделать в UIScrollView и там тоже есть простое решение, чисто на констрейнтах
blog.usejournal.com/...​-constraints-c3df8a9244fc

Подписаться на комментарии