Como aplico uma transformação de perspectiva a um UIView?

199

Estou procurando executar uma transformação de perspectiva em um UIView (como visto no coverflow)

Alguém sabe se isso é possível?

Eu investiguei o uso CALayere percorri todos os podcasts do programador pragmático Core Animation, mas ainda não sou mais claro como criar esse tipo de transformação em um iPhone.

Qualquer ajuda, ponteiros ou trechos de código de exemplo seriam realmente apreciados!

Nick Cartwright
fonte
Não estou certo se isso é adequado para u ou não, mas quando eu faço animação, achei m14 é melhor para mim Apenas para referência :)
b123400
Obrigado! - quais valores você atribui a isso?
Nick Cartwright
1
Ao definir m14, você está distorcendo o eixo da vista no eixo X de maneira estranha. A matemática é perfeitamente válida, mas isso tornará as coisas confusas no caso geral.
fofo
Um muito, muito bom artigo sobre a transformação e perspectiva CALayer 3D, incluindo uma explicação completa do campo m34, pode ser encontrado neste excelente artigo: 3d-model-core-animation
Markus

Respostas:

329

Como Ben disse, você precisará trabalhar com a UIView'scamada, usando a CATransform3Dpara executar a layer's rotation. O truque para fazer funcionar a perspectiva, como descrito aqui , é acessar diretamente uma das matrizes cellsda CATransform3D(m34). A matemática matricial nunca foi minha coisa, então não posso explicar exatamente por que isso funciona, mas funciona. Você precisará definir esse valor como uma fração negativa para sua transformação inicial e aplicar as transformações de rotação da camada a isso. Você também deve poder fazer o seguinte:

Objetivo-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
}

que reconstrói a transformação da camada do zero para cada rotação.

Um exemplo completo disso (com código) pode ser encontrado aqui , onde eu implementei a rotação e o dimensionamento baseados em toque em alguns CALayers, com base em um exemplo de Bill Dudney. A versão mais recente do programa, na parte inferior da página, implementa esse tipo de operação em perspectiva. O código deve ser razoavelmente simples de ler.

A que sublayerTransformvocê se refere na sua resposta é uma transformação aplicada às subcamadas da sua UIView's CALayer. Se você não possui subcamadas, não se preocupe. Eu uso o sublayerTransform no meu exemplo, simplesmente porque existem dois CALayerscontidos na camada que estou girando.

Brad Larson
fonte
1
Obrigado Brad - você é uma estrela. PS: Desculpe pelo atraso da sua excelente resposta! Usuario.
Nick Cartwright
Isso funciona muito bem para mim, exceto que eu pareço perder metade da minha imagem (ao longo de uma divisão vertical) - às vezes LHS, às vezes RHS.
iPadDeveloper2011
18
@ iPadDeveloper2011 - As probabilidades são de que você está tentando girar uma vista ou camada quando houver outra vista ou camada opaca no mesmo plano. A metade da sua visualização ou camada que se projeta para fora da tela fica abaixo dessa outra visualização e, portanto, fica oculta. Você pode reordenar sua hierarquia de visualizações para evitar isso ou usar a propriedade zPosition para mover sua visão de primeiro plano alto o suficiente acima da que está sendo cortada.
Brad Larson
26
FYI, a razão pela qual a célula m34 afeta a transformação de perspectiva é a célula na matriz que afeta como o valor Z do espaço mundial é mapeado para o valor W do espaço de clipe (que é usado para projeção em perspectiva). Leia sobre matrizes de transformação homogêneas para obter mais informações.
fofo
2
@ uerceg - Você vai querer fazer isso como uma pergunta separada, de preferência com imagens que ilustram o que está acontecendo e o que você quer que aconteça.
Brad Larson
7

Você pode usar apenas transformações de gráficos principais (quartzo, somente 2D) aplicadas diretamente à propriedade de transformação de um UIView. Para obter os efeitos no coverflow, você precisará usar o CATransform3D, que é aplicado no espaço 3D, e assim fornecer a vista em perspectiva desejada. Você só pode aplicar o CATransform3Ds em camadas, não em vistas, portanto, será necessário mudar para camadas para isso.

Confira o exemplo "CovertFlow" que acompanha o Xcode. É somente para mac (ou seja, não para iPhone), mas muitos conceitos são bem transferidos.

Ben Gottlieb
fonte
Estou procurando este exemplo de coverflow em / Developer / Examples, sem sucesso, onde você pode encontrá-lo?
779 Alex
Na verdade, é "CovertFlow" e está em / Desenvolvedor / Exemplos / Quartzo / Core Animation / "
Ben Gottlieb
3

Para adicionar à resposta de Brad e fornecer um exemplo concreto, empresto minha própria resposta em outro post sobre um tópico semelhante. Na minha resposta, criei um playground Swift simples com o qual você pode baixar e brincar , onde demonstro como criar efeitos de perspectiva de um UIView com uma hierarquia simples de camadas e mostro resultados diferentes entre usar transforme sublayerTransform. Confira.

Aqui estão algumas das imagens do post:

Opção .sublayerTransform

opção .transform

HuaTham
fonte
O código deve ser publicado como texto. É realmente difícil ler o código em uma imagem. Além disso, o código em uma imagem não pode ser referenciado, copiado ou pesquisado.
precisa saber é o seguinte
@ rmaddy, tenho código disponível no link Bitbucket na minha resposta.
HuaTham 23/08/18
Os links ficam ruins ao longo do tempo. É sempre melhor colar o código importante como texto diretamente na resposta.
Rdddy 23/08
-1

Você pode obter um efeito preciso do carrossel usando o iCarousel SDK.

Você pode obter um efeito instantâneo do Cover Flow no iOS usando a maravilhosa e gratuita biblioteca iCarousel. Você pode baixá-lo em https://github.com/nicklockwood/iCarousel e soltá-lo em seu projeto Xcode com bastante facilidade, adicionando um cabeçalho de ponte (escrito em Objective-C).

Se você não adicionou o código Objective-C a um projeto Swift antes, siga estas etapas:

  • Baixe o iCarousel e descompacte-o
  • Vá para a pasta que você descompactou, abra a subpasta iCarousel, selecione iCarousel.he iCarousel.m e arraste-os para a navegação do projeto - esse é o painel esquerdo do Xcode. Logo abaixo do Info.plist, tudo bem.
  • Marque "Copiar itens, se necessário" e clique em Concluir.
  • O Xcode solicitará a mensagem "Deseja configurar um cabeçalho de ponte Objective-C?" Clique em "Criar cabeçalho de ponte" Você deve ver um novo arquivo em seu projeto, chamado YourProjectName-Bridging-Header.h.
  • Adicione esta linha ao arquivo: #import "iCarousel.h"
  • Depois de adicionar o iCarousel ao seu projeto, você pode começar a usá-lo.
  • Certifique-se de estar em conformidade com os protocolos iCarouselDelegate e iCarouselDataSource.

Código de exemplo do 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
    }
Tecnologia
fonte