【问题标题】:How to convert CATransform3DTranslate to CGAffineTransform so it can Mimic a Carousel View如何将 CATransform3DTranslate 转换为 CGAffineTransform 以便模拟轮播视图
【发布时间】:2019-11-17 10:39:46
【问题描述】:

我的问题是关于如何模仿这个 Carousel 视图 Youtube video只使用 UIView 而不是它的层或 CALayer,这意味着实际转换 UIView 自身。

我发现一个堆栈溢出问题实际上能够转换 CATransform3D 转换为 CGAffineTransform。这是一些天才here 写的答案,但我的问题有点独特。

您在下面看到的动画是使用 CALayer 来创建的。我需要创建同样的动画,但要转换 UIView 而不是它的层。

应该是什么样子:

代码(使用图层创建动画): 这需要一个图像卡,它是一个带有图像的 CALayer(),并转换将其放置在图像的轮播中。

注意:当用户平移移动/动画轮播时也会调用 turnCarousel()。

 let transformLayer = CATransformLayer()

 func turnCarousel() {
    guard let transformSubLayers = transformLayer.sublayers else {return}

    let segmentForImageCard = CGFloat(360 / transformSubLayers.count)

    var angleOffset = currentAngle

    for layer in transformSubLayers {
        var transform = CATransform3DIdentity
        transform.m34 = -1 / 500

        transform = CATransform3DRotate(transform, degreeToRadians(deg: angleOffset), 0, 1, 0)
        transform = CATransform3DTranslate(transform, 0, 0, 175)

        CATransaction.setAnimationDuration(0)

        layer.transform = transform

        angleOffset += segmentForImageCard
    }
}

目前的样子:

所以基本上它很接近,但似乎应该被视为在前面的卡片和在后面的卡片存在缩放问题 的轮播。

为此,我所做的是使用 UIImageView 作为轮播的基本视图,然后添加更多 UIImageViews 作为卡片。所以现在我们正在尝试对 UIImageView/UIView 进行转换

代码:

  var carouselTestView = UIImageView()

  func turnCarouselTestCarousel() {
    let segmentForImageCard = CGFloat(360 / carouselTestView.subviews.count)

    var angleOffset = currentAngleTestView

    for subview in carouselTestView.subviews {
        var transform2 = CATransform3DIdentity
        transform2.m34 = -1 / 500

        transform2 = CATransform3DRotate(transform2, degreeToRadians(deg: angleOffset), 0, 1, 0)
        transform2 = CATransform3DTranslate(transform2, 0, 0, 175)

        CATransaction.setAnimationDuration(0)

        // m13, m23, m33, m43 are not important since the destination is a flat XY plane.
        // m31, m32 are not important since they would multiply with z = 0.
        // m34 is zeroed here, so that neutralizes foreshortening. We can't avoid that.
        // m44 is implicitly 1 as CGAffineTransform's m33.
        let fullTransform: CATransform3D = transform2
        let affine = CGAffineTransform(a: fullTransform.m11, b: fullTransform.m12, c: fullTransform.m21, d: fullTransform.m22, tx: fullTransform.m41, ty: fullTransform.m42)

        subview.transform = affine

        angleOffset += segmentForImageCard
    }
}

实际构成轮播的子图像是通过这个函数添加的,它只是通过我的资产文件夹中名为 1...6 的图像的 for 循环。

代码:

 func CreateCarousel() {

    carouselTestView.frame.size = CGSize(width: self.view.frame.width, height: self.view.frame.height / 2.9)
    carouselTestView.center = CGPoint(self.view.frame.width * 0.5, self.view.frame.height * 0.5)
    carouselTestView.alpha = 1.0
    carouselTestView.backgroundColor = UIColor.clear
    carouselTestView.isUserInteractionEnabled = true
    self.view.insertSubview(carouselTestView, at: 0)

    for i in 1 ... 6 {
        addImageCardTestCarousel(name: "\(i)")
    }

    // Set the carousel for the first time. So that now we can see it like an actual carousel animation
    turnCarouselTestCarousel()

    let panGestureRecognizerTestCarousel = UIPanGestureRecognizer(target: self, action: #selector(self.performPanActionTestCarousel(recognizer:)))
    panGestureRecognizerTestCarousel.delegate = self
    carouselTestView.addGestureRecognizer(panGestureRecognizerTestCarousel)
  }

addImageCardTestCarousel函数在这里:

代码:

    func addImageCardTestCarousel(name: String) {

    let imageCardSize = CGSize(width: carouselTestView.frame.width / 2, height: carouselTestView.frame.height)
    let cardPanel = UIImageView()
    cardPanel.frame.size = CGSize(width: imageCardSize.width, height: imageCardSize.height)
    cardPanel.frame.origin = CGPoint(carouselTestView.frame.size.width / 2 - imageCardSize.width / 2 , carouselTestView.frame.size.height / 2 - imageCardSize.height / 2)
    guard let imageCardImage = UIImage(named: name) else {return}
    cardPanel.image = imageCardImage
    cardPanel.contentMode = .scaleAspectFill
    cardPanel.layer.masksToBounds = true
    cardPanel.layer.borderColor = UIColor.white.cgColor
    cardPanel.layer.borderWidth = 1
    cardPanel.layer.cornerRadius = cardPanel.frame.height / 50
    carouselTestView.addSubview(cardPanel)
}

目的:

这样做的目的是我想构建一个UI,可以在你看到的旋转卡片上取UIViews,而一个CALayer不能添加一个UIView作为子视图。它只能将 UIView 的图层添加到自己的图层中。所以要解决这个问题,我需要用 UIViews 而不是 CALayers 来实现这个动画。

【问题讨论】:

    标签: ios swift xcode uiview transform


    【解决方案1】:

    我解决了这个看起来在最前面的视图后面的视图实际上是在抓住所有的触摸,即使你在前面触摸一张卡片,后面的卡片也会阻止前面卡片的触摸。所以我做了一个可以计算的函数。哪些视图在前面。然后禁用和启用触摸。就像当 2 张卡片叠在一起时,后面的卡片会阻止前面的卡片获取/userInteraction。

    代码:

       func DetermineFrontViews(view subview: UIView, angle angleOffset: CGFloat) {
    
        let looped = Int(angleOffset / 360) // must round down to Int()
        let loopSubtractingReset = CGFloat(360 * looped) // multiply 360 how ever many times we have looped
    
        let finalangle = angleOffset - loopSubtractingReset
    
        if (finalangle >= -70 && finalangle <= 70) || (finalangle >= 290) || (finalangle <= -260) {
    
            print("In front of view")
    
            if subview.isUserInteractionEnabled == false {
                subview.isUserInteractionEnabled = true
            }
        } else {
            print("Back of view")
            if subview.isUserInteractionEnabled == true {
                subview.isUserInteractionEnabled = false
            }
        }
    }
    

    我将它添加到轮转函数中,以查看它是否可以跟踪第一张卡片是在旋转木马的后面还是前面。

        if subview.layer.name == "1" {
                DetermineFrontViews(view: subview, angle: angleOffset)
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-27
      • 2020-06-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多