如何将透视转换应用到UIView?

时间:2023-01-12 23:30:59

I'm looking to perform a perspective transform on a UIView (such as seen in coverflow)

我希望在UIView上执行透视图(如在coverflow中看到的)

Does anyonew know if this is possible?

有没有人知道这是否可行?

I've investigated using CALayer and have run through all the pragmatic programmer Core Animation podcasts, but I'm still no clearer on how to create this kind of transform on an iPhone.

我已经调查过使用CALayer,并且已经浏览过所有实用的程序员核心动画播客,但是我仍然不清楚如何在iPhone上创建这种转换。

Any help, pointers or example code snippets would be really appreciated!

任何帮助、指针或示例代码片段都将受到感激!

4 个解决方案

#1


320  

As Ben said, you'll need to work with the UIView's layer, using a CATransform3D to perform the layer's rotation. The trick to get perspective working, as described here, is to directly access one of the matrix cells of the CATransform3D (m34). Matrix math has never been my thing, so I can't explain exactly why this works, but it does. You'll need to set this value to a negative fraction for your initial transform, then apply your layer rotation transforms to that. You should also be able to do the following:

正如Ben所说,您需要使用UIView的层,使用CATransform3D来执行层的旋转。如图所示,让透视图工作的技巧是直接访问CATransform3D (m34)的一个矩阵单元。矩阵数学从来都不是我喜欢的东西,所以我不能确切地解释它为什么有效,但它确实有效。对于初始变换,需要将这个值设置为负数,然后对其应用层旋转变换。你还应该能够做到以下几点:

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;

which rebuilds the layer transform from scratch for each rotation.

在每次旋转时,重新构建该层的转换。

A full example of this (with code) can be found here, where I've implemented touch-based rotation and scaling on a couple of CALayers, based on an example by Bill Dudney. The newest version of the program, at the very bottom of the page, implements this kind of perspective operation. The code should be reasonably simple to read.

这里有一个完整的示例(带有代码),我在其中基于Bill Dudney的示例实现了基于触摸的旋转和伸缩。程序的最新版本,在页面的最底部,实现了这种透视图操作。代码读起来应该相当简单。

The sublayerTransform you refer to in your response is a transform that is applied to the sublayers of your UIView's CALayer. If you don't have any sublayers, don't worry about it. I use the sublayerTransform in my example simply because there are two CALayers contained within the one layer that I'm rotating.

您在响应中引用的sublayerTransform是一个应用于您的UIView的CALayer的sublayers的转换。如果你没有任何附属品,不要担心。我在示例中使用了次blayertransform,仅仅是因为在我正在旋转的一层中包含两个CALayers。

#2


7  

You can only use Core Graphics (Quartz, 2D only) transforms directly applied to a UIView's transform property. To get the effects in coverflow, you'll have to use CATransform3D, which are applied in 3-D space, and so can give you the perspective view you want. You can only apply CATransform3Ds to layers, not views, so you're going to have to switch to layers for this.

只能使用核心图形(Quartz, 2D)转换直接应用于UIView的转换属性。为了在coverflow中获得效果,你必须使用CATransform3D,它在三维空间中应用,所以可以给你想要的透视图。只能将CATransform3Ds应用于层,而不能应用于视图,因此必须为此切换到层。

Check out the "CovertFlow" sample that comes with Xcode. It's mac-only (ie not for iPhone), but a lot of the concepts transfer well.

查看带有Xcode的“CovertFlow”示例。它只适用于mac(不适用于iPhone),但很多概念都很好地转换了。

#3


3  

To add to Brad's answer and to provide a concrete example, I borrow from my own answer in another post on a similar topic. In my answer, I created a simple Swift playground that you can download and play with, where I demonstrate how to create perspective effects of a UIView with a simple hierarchy of layers, and I show different results between using transform and sublayerTransform. Check it out.

为了补充Brad的答案,并提供一个具体的例子,我在另一篇类似主题的文章中借用了我自己的答案。在我的回答中,我创建了一个简单的快速游乐场,您可以下载并使用它玩,在这里,我演示了如何使用简单的层次结构创建UIView的透视图效果,并展示了使用transform和subblayertransform之间的不同结果。检查出来。

Here are some of the images from the post:

以下是来自邮报的一些图片:

如何将透视转换应用到UIView?

如何将透视转换应用到UIView?

#4


0  

You can get accurate Carousel effect using iCarousel SDK.

您可以使用iCarousel SDK获得准确的旋转木马效果。

You can get an instant Cover Flow effect on iOS by using the marvelous and free iCarousel library. You can download it from https://github.com/nicklockwood/iCarousel and drop it into your Xcode project fairly easily by adding a bridging header (it's written in Objective-C).

通过使用非凡的免费iCarousel库,你可以在iOS上获得即时的封面流效果。您可以从https://github.com/nicklockwood/iCarousel下载它,并通过添加一个桥接头(它是用Objective-C编写的)很容易地将它放到您的Xcode项目中。

If you haven't added Objective-C code to a Swift project before, follow these steps:

如果您以前没有在Swift项目中添加Objective-C代码,请遵循以下步骤:

  • Download iCarousel and unzip it
  • 下载iCarousel并解压它
  • Go into the folder you unzipped, open its iCarousel subfolder, then select iCarousel.h and iCarousel.m and drag them into your project navigation – that's the left pane in Xcode. Just below Info.plist is fine.
  • 进入你打开的文件夹,打开它的iCarousel子文件夹,然后选择iCarousel。h和iCarousel。将它们拖放到项目导航中——这是Xcode中的左窗格。下面信息。plist很好。
  • Check "Copy items if needed" then click Finish.
  • 检查“如果需要复制项目”,然后单击Finish。
  • Xcode will prompt you with the message "Would you like to configure an Objective-C bridging header?" Click "Create Bridging Header" You should see a new file in your project, named YourProjectName-Bridging-Header.h.
  • Xcode将提示您“是否要配置Objective-C桥接头?”单击“创建桥接头”,您将在项目中看到一个新的文件,命名为YourProjectName-Bridging-Header.h。
  • Add this line to the file: #import "iCarousel.h"
  • 将这一行添加到文件:#import“iCarousel.h”
  • Once you've added iCarousel to your project you can start using it.
  • 一旦将iCarousel添加到项目中,就可以开始使用它了。
  • Make sure you conform to both the iCarouselDelegate and iCarouselDataSource protocols.
  • 确保您符合iCarouselDelegate和iCarouselDataSource协议。

Swift 3 Sample Code:

斯威夫特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
    }

#1


320  

As Ben said, you'll need to work with the UIView's layer, using a CATransform3D to perform the layer's rotation. The trick to get perspective working, as described here, is to directly access one of the matrix cells of the CATransform3D (m34). Matrix math has never been my thing, so I can't explain exactly why this works, but it does. You'll need to set this value to a negative fraction for your initial transform, then apply your layer rotation transforms to that. You should also be able to do the following:

正如Ben所说,您需要使用UIView的层,使用CATransform3D来执行层的旋转。如图所示,让透视图工作的技巧是直接访问CATransform3D (m34)的一个矩阵单元。矩阵数学从来都不是我喜欢的东西,所以我不能确切地解释它为什么有效,但它确实有效。对于初始变换,需要将这个值设置为负数,然后对其应用层旋转变换。你还应该能够做到以下几点:

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;

which rebuilds the layer transform from scratch for each rotation.

在每次旋转时,重新构建该层的转换。

A full example of this (with code) can be found here, where I've implemented touch-based rotation and scaling on a couple of CALayers, based on an example by Bill Dudney. The newest version of the program, at the very bottom of the page, implements this kind of perspective operation. The code should be reasonably simple to read.

这里有一个完整的示例(带有代码),我在其中基于Bill Dudney的示例实现了基于触摸的旋转和伸缩。程序的最新版本,在页面的最底部,实现了这种透视图操作。代码读起来应该相当简单。

The sublayerTransform you refer to in your response is a transform that is applied to the sublayers of your UIView's CALayer. If you don't have any sublayers, don't worry about it. I use the sublayerTransform in my example simply because there are two CALayers contained within the one layer that I'm rotating.

您在响应中引用的sublayerTransform是一个应用于您的UIView的CALayer的sublayers的转换。如果你没有任何附属品,不要担心。我在示例中使用了次blayertransform,仅仅是因为在我正在旋转的一层中包含两个CALayers。

#2


7  

You can only use Core Graphics (Quartz, 2D only) transforms directly applied to a UIView's transform property. To get the effects in coverflow, you'll have to use CATransform3D, which are applied in 3-D space, and so can give you the perspective view you want. You can only apply CATransform3Ds to layers, not views, so you're going to have to switch to layers for this.

只能使用核心图形(Quartz, 2D)转换直接应用于UIView的转换属性。为了在coverflow中获得效果,你必须使用CATransform3D,它在三维空间中应用,所以可以给你想要的透视图。只能将CATransform3Ds应用于层,而不能应用于视图,因此必须为此切换到层。

Check out the "CovertFlow" sample that comes with Xcode. It's mac-only (ie not for iPhone), but a lot of the concepts transfer well.

查看带有Xcode的“CovertFlow”示例。它只适用于mac(不适用于iPhone),但很多概念都很好地转换了。

#3


3  

To add to Brad's answer and to provide a concrete example, I borrow from my own answer in another post on a similar topic. In my answer, I created a simple Swift playground that you can download and play with, where I demonstrate how to create perspective effects of a UIView with a simple hierarchy of layers, and I show different results between using transform and sublayerTransform. Check it out.

为了补充Brad的答案,并提供一个具体的例子,我在另一篇类似主题的文章中借用了我自己的答案。在我的回答中,我创建了一个简单的快速游乐场,您可以下载并使用它玩,在这里,我演示了如何使用简单的层次结构创建UIView的透视图效果,并展示了使用transform和subblayertransform之间的不同结果。检查出来。

Here are some of the images from the post:

以下是来自邮报的一些图片:

如何将透视转换应用到UIView?

如何将透视转换应用到UIView?

#4


0  

You can get accurate Carousel effect using iCarousel SDK.

您可以使用iCarousel SDK获得准确的旋转木马效果。

You can get an instant Cover Flow effect on iOS by using the marvelous and free iCarousel library. You can download it from https://github.com/nicklockwood/iCarousel and drop it into your Xcode project fairly easily by adding a bridging header (it's written in Objective-C).

通过使用非凡的免费iCarousel库,你可以在iOS上获得即时的封面流效果。您可以从https://github.com/nicklockwood/iCarousel下载它,并通过添加一个桥接头(它是用Objective-C编写的)很容易地将它放到您的Xcode项目中。

If you haven't added Objective-C code to a Swift project before, follow these steps:

如果您以前没有在Swift项目中添加Objective-C代码,请遵循以下步骤:

  • Download iCarousel and unzip it
  • 下载iCarousel并解压它
  • Go into the folder you unzipped, open its iCarousel subfolder, then select iCarousel.h and iCarousel.m and drag them into your project navigation – that's the left pane in Xcode. Just below Info.plist is fine.
  • 进入你打开的文件夹,打开它的iCarousel子文件夹,然后选择iCarousel。h和iCarousel。将它们拖放到项目导航中——这是Xcode中的左窗格。下面信息。plist很好。
  • Check "Copy items if needed" then click Finish.
  • 检查“如果需要复制项目”,然后单击Finish。
  • Xcode will prompt you with the message "Would you like to configure an Objective-C bridging header?" Click "Create Bridging Header" You should see a new file in your project, named YourProjectName-Bridging-Header.h.
  • Xcode将提示您“是否要配置Objective-C桥接头?”单击“创建桥接头”,您将在项目中看到一个新的文件,命名为YourProjectName-Bridging-Header.h。
  • Add this line to the file: #import "iCarousel.h"
  • 将这一行添加到文件:#import“iCarousel.h”
  • Once you've added iCarousel to your project you can start using it.
  • 一旦将iCarousel添加到项目中,就可以开始使用它了。
  • Make sure you conform to both the iCarouselDelegate and iCarouselDataSource protocols.
  • 确保您符合iCarouselDelegate和iCarouselDataSource协议。

Swift 3 Sample Code:

斯威夫特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
    }