Site icon AppTractor

“Дырявим” вьюхи на Swift

Алексей Михайлов в свое блоге рассказал о том,  как из одного UIView увидеть другой. 

Вопрос: Что еще за “дырка” такая?

Ответ: Условимся, что дыркой будем называть часть UIView, которая будет вырезана для того, чтобы можно было смотреть как бы “сквозь” UIView.

Рис 1. Дырка

На рисунке 1 представлена иерархия дырки. Cлева направо:

Как такое сделать?

Засунуть в viewDidLoad() функцию makeHole():

func makeHole() {
// Берем путь у whiteView

   let path = UIBezierPath(rect: whiteView.bounds)

// Создаем путь у holeView и скругляем его

   let pathWithRadius = UIBezierPath(
      roundedRect: holeView.frame, 
      byRoundingCorners: [.allCorners], 
      cornerRadii: holeView.frame.size)

// Создаем общую геометрию пути

   path.append(pathWithRadius)

// Создаем маску этой геометрии и применяем правило заливки evenOdd, которое как раз и делает дырку

   let mask = CAShapeLayer()
   mask.path = path.cgPath
   mask.fillRule = CAShapeLayerFillRule.evenOdd

// Применяем полученную маску к whiteView

   whiteView.layer.mask = mask
}

Для тех, кто плохо воспринимает код по снипетам, сразу прикладываю исходный код проекта.

Теперь для создания дырявой UITableView надо создать HoleTableViewCell, которая будет повторять иерархию с первого рисунка и в реализации содержать аналогичную функцию makeHole(). Вызывать ее будем в методе делегата tableView(_:willDisplay:forRowAt:):

func tableView(_ tableView: UITableView,
      willDisplay cell: UITableViewCell,
      forRowAt indexPath: IndexPath) {

   if let holeCell = cell as? HoleTableViewCell {

      holeCell.layoutIfNeeded()
      holeCell.makeHole()
   }
}

 

Рис 2. Дырявый UITableView

Отлично! Но по задумке дизайнера нужно двигаться дальше, я посмотрел в фигму и увидел следующий паттерн:

Рис 3. Паттерн выреза

С помощью extension к UIImageView реализуется логическое вычитание XOR. Оно делает дырку в image по заданному паттерну:

extension UIImageView {
   func applyGradientPattern(_ pattern: UIImage) {
      let size = frame.size
      let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
      UIGraphicsBeginImageContext(size)
      self.image!.draw(in: rect)
      pattern.draw(in: rect, blendMode: .destinationOut, alpha: 1.0)
      let newImage = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()
      self.image = newImage
   }
}

И вызвав эту функцию в методе делегата tableView(_:willDisplay:forRowAt:):

func tableView(_ tableView: UITableView, 
      willDisplay cell: UITableViewCell, 
      forRowAt indexPath: IndexPath) {

   if let gradientHoleCell = cell as? GradientHoleTableViewCell {

      gradientHoleCell.layoutIfNeeded()
      gradientHoleCell.applyGradientPattern()
      gradientHoleCell.makeHole()
   }
}

Получил следующий результат:

Рис 4. Градиентно дырявый UITableView

Что произошло простыми словами?

Мы сделали дырку в whiteView размером с holeImageView, а потом дырку в holeImageView по полученному от дизайнера паттерну.

В процессе работы над спринтом естественно всплыло несколько косяков.

Например, если общая сумма высот ячеек меньше высоты экрана без safeArea, то происходило следующее:

Рис 5. Маленькое количество ячеек

Проблема решалась подсчетом высоты контента и

tableView.bounces = false

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

Надеюсь было интересно! )

Тут можно глянуть код проекта.

И я открыт к любым вопросам по этой теме.

P.S. У нас в mileonair, в разделе акций, можно увидеть дырку идущую сквозь UIImageView, UITableView и UICollectionView одновременно

Источник

Exit mobile version