如何将透视变换应用于UIView?

时间:2008-12-07 16:04:39

标签: ios cocoa-touch uiview core-animation calayer

我正在寻找对UIView执行透视变换(例如在coverflow中看到)

有谁知道这是否可行?

我已经使用CALayer进行了调查,并且已经完成了所有实用的程序员Core Animation播客,但我仍然不清楚如何在iPhone上创建这种转换。

任何帮助,指针或示例代码片段都会非常感激!

5 个答案:

答案 0 :(得分:323)

正如Ben所说,你需要使用UIView的图层,使用CATransform3D来执行图层的旋转。获得透视工作的技巧,如described here,是直接访问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;

为每次旋转从头开始重建图层变换。

基于Bill Dudney的一个例子,可以找到here的完整示例{{3}},其中我已经在几个CALayers上实现了基于触摸的旋转和缩放。该程序的最新版本位于页面的最底部,实现了这种透视操作。代码应该相当简单易读。

您在回复中引用的sublayerTransform是一个应用于UIView CALayer的子图层的变换。如果您没有任何子图层,请不要担心。我在我的例子中使用sublayerTransform只是因为我正在旋转的一层中包含两个CALayer。

答案 1 :(得分:7)

您只能使用直接应用于UIView的transform属性的Core Graphics(仅Quartz,2D)变换。要在coverflow中获得效果,您必须使用CATransform3D,它们应用于三维空间,因此可以为您提供所需的透视图。您只能将CATransform3D应用于图层而不是视图,因此您必须切换到图层。

查看Xcode附带的“CovertFlow”示例。它只是mac(即不适用于iPhone),但很多概念转移得很好。

答案 2 :(得分:3)

为了增加Brad的答案并提供一个具体的例子,我在另一篇关于类似主题的帖子中借用my own answer。在我的回答中,我创建了一个简单的Swift游乐场,你可以download and play with,在那里我演示了如何使用简单的层次结构创建UIView的透视效果,并且我在使用transform之间显示了不同的结果。 sublayerTransform。看看吧。

以下是帖子中的一些图片:

.sublayerTransform option

.transform option

答案 3 :(得分:0)

Swift 5.0

func makeTransform(horizontalDegree: CGFloat, verticalDegree: CGFloat, maxVertical: CGFloat,rotateDegree: CGFloat, maxHorizontal: CGFloat) -> CATransform3D {
    var transform = CATransform3DIdentity
           
    transform.m34 = 1 / -500
    
    let xAnchor = (horizontalDegree / (2 * maxHorizontal)) + 0.5
    let yAnchor = (verticalDegree / (-2 * maxVertical)) + 0.5
    let anchor = CGPoint(x: xAnchor, y: yAnchor)
    
    setAnchorPoint(anchorPoint: anchor, forView: self.imgView)
    let hDegree  = (CGFloat(horizontalDegree) * .pi)  / 180
    let vDegree  = (CGFloat(verticalDegree) * .pi)  / 180
    let rDegree  = (CGFloat(rotateDegree) * .pi)  / 180
    transform = CATransform3DRotate(transform, vDegree , 1, 0, 0)
    transform = CATransform3DRotate(transform, hDegree , 0, 1, 0)
    transform = CATransform3DRotate(transform, rDegree , 0, 0, 1)
    
    return transform
}

func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
    var newPoint = CGPoint(x: view.bounds.size.width * anchorPoint.x, y: view.bounds.size.height * anchorPoint.y)
    var oldPoint = CGPoint(x: view.bounds.size.width * view.layer.anchorPoint.x, y: view.bounds.size.height * view.layer.anchorPoint.y)
    
    newPoint = newPoint.applying(view.transform)
    oldPoint = oldPoint.applying(view.transform)
    
    var position = view.layer.position
    position.x -= oldPoint.x
    position.x += newPoint.x
    
    position.y -= oldPoint.y
    position.y += newPoint.y
    
    print("Anchor: \(anchorPoint)")
    
    view.layer.position = position
    view.layer.anchorPoint = anchorPoint
}

你只需要用你的学位来调用这个函数。例如:

var transform = makeTransform(horizontalDegree: 20.0 , verticalDegree: 25.0, maxVertical: 25, rotateDegree: 20, maxHorizontal: 25)
imgView.layer.transform = transform

答案 4 :(得分:-1)

您可以使用iCarousel SDK获得准确的轮播效果。

您可以使用精彩且免费的iCarousel库在iOS上获得即时Cover Flow效果。您可以从https://github.com/nicklockwood/iCarousel下载它,并通过添加桥接标头(用Objective-C编写)将其放入Xcode项目中。

如果您之前没有将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
    }