Как применить преобразование перспективы к UIView?

Я хочу выполнить преобразование перспективы на UIView (например, видимое в обложке)

Знает ли кто-нибудь, возможно ли это?

Я исследовал с помощью CALayer и пропустил все прагматические программные подкасты Core Animation, но я все еще не понимаю, как создать такой тип преобразования на iPhone.

Любые справки, указатели или фрагменты кода кода будут действительно оценены!

Ответы

Ответ 1

Как сказал Бен, вам нужно будет работать со слоем UIView, используя CATransform3D для выполнения layer rotation. Хитрость для получения перспективной работы, как описано здесь, состоит в том, чтобы получить прямой доступ к одной из матриц cells CATransform3D (m34). Математическая математика никогда не была моей вещью, поэтому я не могу точно объяснить, почему это работает, но это работает. Вам нужно будет установить это значение в отрицательную дробь для вашего первоначального преобразования, а затем применить к нему преобразования вращения слоя. Вы также должны быть в состоянии сделать следующее:

Objective-C

UIView *myView = [[self subviews] objectAtIndex:0];
CALayer *layer = myView.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 45.0f * M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;

Swift 5.0

if let myView = self.subviews.first {
    let layer = myView.layer
    var rotationAndPerspectiveTransform = CATransform3DIdentity
    rotationAndPerspectiveTransform.m34 = 1.0 / -500
    rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 45.0 * .pi / 180.0, 0.0, 1.0, 0.0)
    layer.transform = rotationAndPerspectiveTransform
}

который перестраивает преобразование слоя с нуля для каждого поворота.

Полный пример этого (с кодом) можно найти здесь, где я реализовал ротацию и масштабирование на основе касания для пары CALayers, основываясь на примере Билла Дадни. Самая новая версия программы, в самом низу страницы, реализует такую перспективную операцию. Код должен быть достаточно простым для чтения.

sublayerTransform, на который вы ссылаетесь в своем ответе, является преобразованием, которое применяется к подуровням вашего UIView CALayer. Если у вас нет подслоев, не беспокойтесь об этом. Я использую sublayerTransform в моем примере просто потому, что в одном вращаемом слое содержатся два CALayers.

Ответ 2

Очень хорошая статья о 3D-преобразовании CALayer и перспективах, включая подробное объяснение поля m34, можно найти в этой замечательной статье:

core-animation-3d-model

Ответ 3

Вы можете использовать преобразования Core Graphics (Quartz, 2D only) напрямую, применяемые к свойству преобразования UIView. Чтобы получить эффекты в потолочном покрытии, вам придется использовать CATransform3D, которые применяются в трехмерном пространстве, и поэтому могут дать вам перспективный вид. Вы можете применять CATransform3D только к слоям, а не к представлениям, поэтому вам придется переключаться на слои для этого.

Проверьте образец "CovertFlow", который поставляется с Xcode. Это mac-only (то есть не для iPhone), но многие концепции хорошо переносятся.

Ответ 4

Чтобы добавить к ответу Брэда и привести конкретный пример, я заимствую свой ответ в другом посте на аналогичную тему. В своем ответе я создал простую игровую площадку Swift, с которой вы можете загружать и играть, где я демонстрирую, как создавать эффекты перспективы UIView с простой иерархией слоев, и я показываю разные результаты между использованием transform и sublayerTransform. Проверьте это.

Вот некоторые изображения из поста:

.sublayerTransform option

.transform option

Ответ 5

Вы можете получить точный эффект Карусели, используя iCarousel SDK.

Вы можете получить мгновенный эффект Cover Flow на iOS с помощью великолепной и бесплатной библиотеки iCarousel. Вы можете скачать его с https://github.com/nicklockwood/iCarousel и довольно легко добавить в свой проект Xcode, добавив соединительный заголовок (он написан на Objective-C).

Если вы ранее не добавляли код Objective-C в проект Swift, выполните следующие действия:

  • Скачайте iCarousel и распакуйте его
  • Перейдите в разархивированную папку, откройте ее подпапку iCarousel, затем выберите iCarousel.h и iCarousel.m и перетащите их в навигацию проекта - на левую панель в Xcode. Чуть ниже Info.plist в порядке.
  • Установите флажок "Копировать элементы при необходимости", затем нажмите "Готово".
  • Xcode предложит вам сообщение "Хотите настроить заголовок моста Objective-C?" Нажмите "Создать заголовок моста". В вашем проекте вы увидите новый файл с именем YourProjectName-Bridging-Header.h.
  • Добавьте эту строку в файл: #import "iCarousel.h"
  • Как только вы добавили iCarousel в свой проект, вы можете начать его использовать.
  • Убедитесь, что вы соответствуете протоколам iCarouselDelegate и iCarouselDataSource.

Образец кода Swift 3:

    override func viewDidLoad() {
      super.viewDidLoad()
      let carousel = iCarousel(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
      carousel.dataSource = self
      carousel.type = .coverFlow
      view.addSubview(carousel) 
    }

   func numberOfItems(in carousel: iCarousel) -> Int {
        return 10
    }

    func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
        let imageView: UIImageView

        if view != nil {
            imageView = view as! UIImageView
        } else {
            imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 128, height: 128))
        }

        imageView.image = UIImage(named: "example")

        return imageView
    }