UIImages之间的立方体过渡

时间:2016-01-19 21:25:00

标签: ios swift uiimage

我想创建一个视图,当水平滚动时,在具有立方体动画效果的UIImage个对象数组之间进行转换。例如:

enter image description here

有人可以指出我在正确的方向上如何在Swift中使用立方体过渡动画水平滚动UIImage个对象数组吗?

2 个答案:

答案 0 :(得分:4)

它太宽泛而无法解释,但你可以使用这个UIViewController:

class CubeScrollViewController: UIViewController,UIScrollViewDelegate {
    var scrollView:UIScrollView?
    var images:[UIImage] = [UIImage]()
    var imageViews:[IntegerLiteralType:UIImageView] = [IntegerLiteralType:UIImageView]()
    var currentIndex = 0
    var scrollOffset:CGFloat = 0.0
    var previousOffset:CGFloat = 0.0
    var suppressScrollEvent:Bool = false
    var add = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        self.images = [UIImage(named: "image1")!,UIImage(named: "image2")!,UIImage(named:"image3")!,UIImage(named: "image4")!]

    }

    override func viewDidLayoutSubviews()
    {
        super.viewDidLayoutSubviews()
        scrollView?.removeFromSuperview()
        scrollView = UIScrollView(frame: self.view.frame)
        scrollView?.autoresizingMask = [.FlexibleWidth,.FlexibleHeight]
        scrollView?.showsHorizontalScrollIndicator = true
        scrollView?.pagingEnabled = true
        scrollView?.directionalLockEnabled = true;
        scrollView?.autoresizesSubviews = false;
        scrollView?.delegate = self
        self.view.addSubview(scrollView!)
        var index = 0
        for image in self.images
        {
            let imageView = UIImageView(frame: self.view.frame)
            imageView.contentMode = .ScaleAspectFill
            imageView.clipsToBounds = true
            imageView.image = image
            imageView.backgroundColor = UIColor.whiteColor()
            self.imageViews[index] = imageView
            index += 1
        }
        var pages = self.images.count
        if self.images.count > 1
        {
            pages += 2
        }
        self.suppressScrollEvent = true
        self.scrollView?.contentSize = CGSize(width: self.view.bounds.size.width * CGFloat(pages), height: self.view.bounds.size.height)
        self.suppressScrollEvent = false
        self.updateContentOffset()
        self.loadUnloadImageViews()
        self.updateLayout()
    }

    func setCurrentImageIndex(currentImageIndex:IntegerLiteralType)
    {
        self.scrollToImageAtIndex(currentImageIndex,animated:true)
    }

    func scrollToImageAtIndex(index:IntegerLiteralType,animated:Bool)
    {
        var offset = index
        if offset > self.images.count
        {
            offset = offset % self.images.count
        }
        offset = max(-1, offset)+1
        scrollView?.setContentOffset(CGPoint(x: self.view.bounds.size.width * CGFloat(offset),y: 0),animated: animated)
    }

    func scrollForward(animated:Bool)
    {
        self.scrollToImageAtIndex(self.currentIndex+1, animated: animated)
    }

    func scrollBack(animated:Bool)
    {
        self.scrollToImageAtIndex(self.currentIndex-1, animated: animated)
    }

    func reloadData()
    {
        for view:UIImageView in self.imageViews.values
        {
            view.removeFromSuperview()
        }
    }

    func reloadImageAtIndex(index:IntegerLiteralType,animated:Bool)
    {
        let image = self.images[index]
        let oldImageView = self.imageViews[index]
        let imageView = UIImageView(frame: self.view.frame)
        imageView.contentMode = .ScaleAspectFill
        imageView.clipsToBounds = true
        imageView.image = image
        imageView.backgroundColor = UIColor.whiteColor()
        let transform = imageView.layer.transform
        let center = imageView.center

        if animated
        {
            let animation = CATransition()
            animation.type = kCATransitionFade
            self.scrollView?.layer.addAnimation(animation, forKey: nil)
        }
        oldImageView!.removeFromSuperview()
        self.scrollView?.addSubview(imageView)
        imageView.layer.transform = transform
        imageView.center = center
    }

    func updateContentOffset()
    {
        var offset = self.scrollOffset
        if self.images.count>1
        {
            offset+=1.0
            while offset<1.0
            {
                offset+=1.0
            }
            while offset>=CGFloat(self.images.count+1)
            {
                offset-=CGFloat(self.images.count)
            }
        }
        self.previousOffset = offset

        self.suppressScrollEvent = true
        self.scrollView?.contentOffset = CGPointMake(self.view.bounds.size.width*offset, 0.0)
        self.suppressScrollEvent = false
    }

    func updateLayout()
    {
        for index in self.imageViews.keys
        {
            let imageView = self.imageViews[index]
            if imageView != nil && imageView!.superview == nil
            {
                imageView?.layer.doubleSided = false
                self.scrollView?.addSubview(imageView!)
                self.add++
            }
            var angle = (self.scrollOffset - CGFloat(index)) * CGFloat(M_PI_2)
            while angle < 0
            {
                angle = angle + CGFloat(M_PI * 2.0)
            }
            while angle > CGFloat(M_PI*2.0)
            {
                angle = angle - CGFloat(M_PI * 2.0)
            }
            var transform = CATransform3DIdentity
            if angle != 0.0
            {
                transform.m34 = -1.0/500;
                transform = CATransform3DTranslate(transform, 0.0, 0.0, -self.view.bounds.size.width / 2.0)
                transform = CATransform3DRotate(transform, -angle, 0, 1, 0)
                transform = CATransform3DTranslate(transform, 0, 0, self.view.bounds.size.width / 2.0)
            }

            imageView?.bounds = self.view.bounds
            imageView?.center = CGPoint(x: self.view.bounds.size.width * 0.5 + self.scrollView!.contentOffset.x, y: self.view.bounds.size.height * 0.5);
            imageView?.layer.transform = transform
        }
    }

    func loadUnloadImageViews()
    {
        var visibleIndices = [IntegerLiteralType]()
        visibleIndices.append(self.currentIndex)
        visibleIndices.append(self.currentIndex + 1)

        if self.currentIndex > 0
        {
            visibleIndices.append(self.currentIndex - 1)
        }
        else
        {
            visibleIndices.append(-1)
        }

        for index in 0...self.images.count
        {
            if !visibleIndices.contains(index)
            {
                let imageView = self.imageViews[index]
                imageView?.removeFromSuperview()
                self.imageViews.removeValueForKey(index)
            }
        }
        for index in visibleIndices
        {
            var imageView:UIImageView? = nil
            if self.imageViews[index] != nil
            {
                imageView = self.imageViews[index]!
            }
            if imageView == nil && self.images.count > 0
            {
                let newIndex = (index + self.images.count) % self.images.count
                let imageView = UIImageView(frame: self.view.frame)
                imageView.contentMode = .ScaleAspectFill
                imageView.clipsToBounds = true
                imageView.backgroundColor = UIColor.whiteColor()
                imageView.image = self.images[newIndex]
                self.imageViews[index] = imageView
            }
        }

    }

    func scrollViewDidScroll(scrollView: UIScrollView) {
        if !self.suppressScrollEvent
        {
            let offset:CGFloat = scrollView.contentOffset.x / self.view.bounds.size.width
            self.scrollOffset += (offset - self.previousOffset)
            while self.scrollOffset < 0.0
            {
                self.scrollOffset += CGFloat(self.images.count)
            }
            while self.scrollOffset >= CGFloat(self.images.count)
            {
                self.scrollOffset -= CGFloat(self.images.count)
            }
            self.previousOffset = offset

            if offset - floor(offset) == 0.0
            {
                self.scrollOffset = round(self.scrollOffset)
            }

            self.currentIndex = max(0, min(self.images.count - 1, IntegerLiteralType(round(self.scrollOffset))))
            self.updateContentOffset()
            self.loadUnloadImageViews()
            self.updateLayout()

        }
    }

    func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
        let nearestIntegralOffset = round(self.scrollOffset)
        if abs(self.scrollOffset - nearestIntegralOffset) > 0.0
        {
            self.scrollToImageAtIndex(self.currentIndex, animated: true)
        }
    }

}

将多维数据集中所需的图像设置为self.images。当前实现包装图像,这意味着当您在第一个图像上向左滑动时,最后一个图像出现,并在最后一个图像上向右滑动,第一个图像出现。

Swift 3.0

import UIKit

public class CubeScrollViewController: UIViewController
{
    //MARK: - Properties
    private lazy var scrollView: UIScrollView =
    {
        let scrollView = UIScrollView()
        scrollView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
        scrollView.showsHorizontalScrollIndicator = true
        scrollView.isPagingEnabled = true
        scrollView.isDirectionalLockEnabled = true;
        scrollView.autoresizesSubviews = false;
        scrollView.delegate = self
        return scrollView
    }()
    var images = [UIImage]()
    fileprivate var imageViews = [Int: UIImageView]()
    fileprivate var currentIndex = 0
    fileprivate var scrollOffset: CGFloat = 0.0
    fileprivate var previousOffset: CGFloat = 0.0
    fileprivate var suppressScrollEvent = false

    //MARK: - Lifecycle
    override func viewDidLoad()
    {
        super.viewDidLoad()
        self.view.addSubview(self.scrollView)
        for (index, image) in self.images.enumerated()
        {
            let imageView = UIImageView(image: image)
            imageView.contentMode = .scaleAspectFill
            imageView.clipsToBounds = true
            imageView.backgroundColor = UIColor.white
            self.imageViews[index] = imageView
        }
    }

    override func viewDidLayoutSubviews()
    {
        super.viewDidLayoutSubviews()
        self.scrollView.frame = self.view.bounds
        self.imageViews.values.forEach {  $0.frame  = self.view.bounds }
        var pages = CGFloat(self.images.count)
        pages = self.images.count > 1 ? pages + 2 : pages
        self.suppressScrollEvent = true
        self.scrollView.contentSize = CGSize(width: self.view.bounds.width * pages, height: self.view.bounds.height)
        self.suppressScrollEvent = false
        self.updateContentOffset()
        self.loadUnloadViews()
        self.updateLayout()
    }


    //MARK: - Exposed Functions
    func set(_ currentImageIndex: Int)
    {
        self.scrollToImage(at: currentIndex)
    }

    func scrollToImage(at index: Int, animated: Bool = true)
    {
        var offset = index > self.images.count ? index % self.images.count : index
        offset = max(-1, offset) + 1
        self.scrollView.setContentOffset(CGPoint(x: self.view.bounds.width * CGFloat(offset), y: 0.0), animated: animated)
    }

    func scrollForward(animated: Bool = true)
    {
        self.scrollToImage(at: self.currentIndex + 1, animated: animated)
    }

    func scrollBack(animated: Bool = true)
    {
        self.scrollToImage(at: self.currentIndex - 1, animated: animated)
    }

    func reloadData()
    {
        self.imageViews.values.forEach { $0.removeFromSuperview() }
    }

    func reloadImage(at index: Int, animated: Bool = true)
    {
        guard 0 ..< self.images.count ~= index else { return }
        let image = self.images[index]
        let oldImageView = self.imageViews[index]
        let imageView = UIImageView(frame: self.view.bounds)
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        imageView.image = image
        imageView.backgroundColor = .white
        let transform = imageView.layer.transform
        let center = imageView.center

        if animated
        {
            let animation = CATransition()
            animation.type = kCATransitionFade
            self.scrollView.layer.add(animation, forKey: nil)
        }
        oldImageView?.removeFromSuperview()
        self.scrollView.addSubview(imageView)
        imageView.layer.transform = transform
        imageView.center = center
    }

    //MARK: - Layout
    fileprivate func updateContentOffset()
    {
        guard self.images.count > 1 else { return }

        var offset = self.scrollOffset
        offset += 1.0
        while offset < 1.0
        {
            offset += 1.0
        }
        while offset >= CGFloat(self.images.count + 1)
        {
            offset -= CGFloat(self.images.count)
        }
        self.previousOffset = offset

        self.suppressScrollEvent = true
        self.scrollView.contentOffset = CGPoint(x: self.view.bounds.width * offset, y: 0.0)
        self.suppressScrollEvent = false

    }

    fileprivate func updateLayout()
    {
        for index in self.imageViews.keys
        {
            guard let imageView = self.imageViews[index] else { continue }
            if imageView.superview == nil
            {
                imageView.layer.isDoubleSided = false
                self.scrollView.addSubview(imageView)
            }

            var angle = (self.scrollOffset - CGFloat(index)) * CGFloat.pi * 0.5
            while angle < 0
            {
                angle += CGFloat.pi * 2.0
            }
            while angle > CGFloat.pi * 2.0
            {
                angle -= CGFloat.pi * 2.0
            }

            var transform = CATransform3DIdentity
            if angle != 0.0
            {
                transform.m34 = -1.0 / 500.0
                transform = CATransform3DTranslate(transform, 0.0, 0.0, -self.view.bounds.width * 0.5)
                transform = CATransform3DRotate(transform, -angle, 0, 1, 0)
                transform = CATransform3DTranslate(transform, 0, 0, self.view.bounds.width * 0.5)
            }

            imageView.bounds = self.view.bounds
            imageView.center = CGPoint(x: self.view.bounds.midX + self.scrollView.contentOffset.x, y: self.view.bounds.midY)
            imageView.layer.transform = transform
        }
    }

    fileprivate func loadUnloadViews()
    {
        var visibleIndices = [Int]()
        visibleIndices.append(self.currentIndex)
        visibleIndices.append(self.currentIndex + 1)

        if self.currentIndex > 0
        {
            visibleIndices.append(self.currentIndex - 1)
        }
        else
        {
            visibleIndices.append(-1)
        }

        for index in 0 ..< self.images.count
        {
            guard !visibleIndices.contains(index) else { continue }

            let imageView = self.imageViews[index]
            imageView?.removeFromSuperview()
            self.imageViews.removeValue(forKey: index)
        }
        for index in visibleIndices
        {
            if let _ = self.imageViews[index]
            {

            }
            else if self.images.count > 0
            {
                let newIndex = (index + self.images.count) % self.images.count
                let imageView = UIImageView(frame: self.view.bounds)
                imageView.contentMode = .scaleAspectFill
                imageView.clipsToBounds = true
                imageView.backgroundColor = .white
                imageView.image = self.images[newIndex]
                self.imageViews[index] = imageView
            }
        }
    }
}

// MARK: - UIScrollViewDelegate
extension CubeScrollViewController: UIScrollViewDelegate
{
    func scrollViewDidScroll(_ scrollView: UIScrollView)
    {
        guard !self.suppressScrollEvent else { return }

        let offset: CGFloat = scrollView.contentOffset.x / self.view.bounds.width
        self.scrollOffset += (offset - self.previousOffset)
        while self.scrollOffset < 0.0
        {
            self.scrollOffset += CGFloat(self.images.count)

        }
        while self.scrollOffset >= CGFloat(self.images.count)
        {
            self.scrollOffset -= CGFloat(self.images.count)

        }
        self.previousOffset = offset

        if offset - floor(offset) == 0.0
        {
            self.scrollOffset = round(self.scrollOffset)

        }
        self.currentIndex = max(0, min(self.images.count - 1, Int(round(self.scrollOffset))))
        self.updateContentOffset()
        self.loadUnloadViews()
        self.updateLayout()
    }

    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)
    {
        let nearestIntegralOffset = round(self.scrollOffset)
        guard abs(self.scrollOffset - nearestIntegralOffset) > 0.0 else { return }
        self.scrollToImage(at: self.currentIndex)
    }
}

答案 1 :(得分:0)

使用Spring animation创建各种动画,包括立方体。并且代码非常少。