【问题标题】:How to create horizontal carousel in iOS 10 Swift 3如何在 iOS 10 Swift 3 中创建水平轮播
【发布时间】:2017-07-28 22:48:11
【问题描述】:

最近我从 Android 世界切换到 iOS。我已经看到了创建水平滑块的两个选项。我的目标是在这张图片上创建一个水平的卡片滑块。 我很困惑 iOS 中哪个元素需要最佳选择。我应该为此使用 CollectionView 还是应该为此使用 PageViewController? 由于我是初学者,我无法在这个阶段使用我所拥有的知识有限的图书馆。请建议我该怎么做

【问题讨论】:

  • 具有自定义流布局的 UICollectionView
  • 我有大约 60-70 个元素,其中每个元素都有图像以显示指示符。如果我使用 Collection 视图,则没有内置的指标功能,但如果我使用 pageviewcontroller,则有提供指标的计数功能。我是否也需要手动编写指标代码?
  • 是的。对于 60-70 个元素,无论如何您都可能需要自定义代码/指标设计,否则屏幕底部会有 60-70 个小点。 CollectionView 非常适合这种设计,因为它出于性能原因重用了单元格。
  • 如果你喜欢使用开源代码,看看 iCarousel

标签: ios swift3


【解决方案1】:

简答

您应该使用 UICollectionView。

更详细的回答

1) 使用UICollectionViewFlowLayout设置横向布局

let layout = UICollectionViewFlowLayout()
// The size of each item. Pick a suitable height so that the items do not get stacked:
layout.itemSize = CGSize(width: view.frame.width / 2, height: view.frame.height)
// The most important part:
layout.scrollDirection = .horizontal
// Then initialize collectionView
let collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)

2) 设置缩放(伪代码)

i.有self实现UIScrollViewDelegate方法scrollViewDidScroll(UIScrollView)

ii.设置collectionView.delgate = self

iii.在scrollViewDidScroll(UIScrollView) 中,通过强制转换scrollView as! UICollectionView 并获取visibleCells 属性来获取所有可见单元格。

iv.对于可见单元格中的单元格,使用CGAffineTransform 给定单元格与屏幕水平中心的距离设置适当的比例(单元格内容,不是单元格本身)。这可能需要一些算术;这应该可以正常工作:

s = −0.00005 * pow(cell.center.x, 2) + 1.5

希望这会有所帮助!如果需要,请随时要求澄清。

【讨论】:

  • 你是如何设置比例的?
  • 是的,我尝试使用CGAffineTransformMakeScale,它不起作用。
  • 对于@Supertecnoboff 和未来参考,请检查对 2.iv 的编辑。
  • 你能解释一下你的缩放公式吗?
  • @AmberK,抱歉回复晚了。这个想法是有一个向下开口的抛物线,它是尺度的函数。我只是通过实验选择了这些值。
【解决方案2】:

我已尝试将 CollectionView 用于轮播幻灯片,但对我来说并不奏效。我不喜欢为了让它在一行中显示图像而不得不采取的骇人听闻的方式,我也不喜欢它无法返回活动图像的事实(这里也有一些解决方法,但他们不看起来可靠)。因此,很自然地,我最终为我的目的构建了一个自定义幻灯片轮播。我将在这里分享代码,希望它可以帮助(或至少指导某人)遇到类似问题。

注意:我的轮播是全宽的,singleImagePerScreen 轮播,带有滑动识别器来滑动图像,并在图像处于活动状态时触发委托功能(我用它来显示活动图像 - “1 of 5”)。

已测试:SWIFT 5、XCode 12.2、iOS 14.2

// ImageCarouselView class

import UIKit

class ImageCarouselView: UIView {
    private let images: [UIImage?]
    private var index = 0
    private let screenWidth = UIScreen.main.bounds.width

    var delegate: ImageCarouselViewDelegate?

    lazy var previousImageView = imageView(image: nil, contentMode: .scaleAspectFit)
    lazy var currentImageView = imageView(image: nil, contentMode: .scaleAspectFit)
    lazy var nextImageView = imageView(image: nil, contentMode: .scaleAspectFit)

    lazy var previousImageLeadingConstraint: NSLayoutConstraint = {
        return previousImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: -screenWidth)
    }()

    lazy var currentImageLeadingConstraint: NSLayoutConstraint = {
        return currentImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0)
    }()

    lazy var nextImageLeadingConstraint: NSLayoutConstraint = {
        return nextImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: screenWidth)
    }()

    init(_ images: [UIImage?]) {
        self.images = images

        super.init(frame: .zero)

        self.translatesAutoresizingMaskIntoConstraints = false

        setupLayout()
        setupImages()
        setupSwipeRecognizer()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupLayout() {
        self.subviews.forEach({ $0.removeFromSuperview() })

        addSubview(previousImageView)
        addSubview(currentImageView)
        addSubview(nextImageView)

        previousImageLeadingConstraint = previousImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: -screenWidth)
        currentImageLeadingConstraint = currentImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0)
        nextImageLeadingConstraint = nextImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: screenWidth)

        NSLayoutConstraint.activate([
            previousImageLeadingConstraint,
            previousImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
            previousImageView.widthAnchor.constraint(equalToConstant: screenWidth),

            currentImageLeadingConstraint,
            currentImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
            currentImageView.widthAnchor.constraint(equalToConstant: screenWidth),

            nextImageLeadingConstraint,
            nextImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
            nextImageView.widthAnchor.constraint(equalToConstant: screenWidth),
        ])
    }

    private func setupImages() {
        currentImageView.image = images[self.index]

        guard images.count > 1 else { return }

        if (index == 0) {
            previousImageView.image = images[images.count - 1]
            nextImageView.image = images[index + 1]
        }

        if (index == (images.count - 1)) {
            previousImageView.image = images[index - 1]
            nextImageView.image = images[0]
        }
    }

    private func setupSwipeRecognizer() {
        guard images.count > 1 else { return }

        let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipes))
        let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipes))

        leftSwipe.direction = .left
        rightSwipe.direction = .right

        self.addGestureRecognizer(leftSwipe)
        self.addGestureRecognizer(rightSwipe)
    }

    @objc private func handleSwipes(_ sender: UISwipeGestureRecognizer) {
        if (sender.direction == .left) {
            showNextImage()
        }

        if (sender.direction == .right) {
            showPreviousImage()
        }
    }

    private func showPreviousImage() {
        previousImageLeadingConstraint.constant = 0
        currentImageLeadingConstraint.constant = screenWidth

        UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: {
            self.layoutIfNeeded()
        }, completion: { _ in
            self.nextImageView = self.currentImageView
            self.currentImageView = self.previousImageView
            self.previousImageView = self.imageView(image: nil, contentMode: .scaleAspectFit)

            self.index = self.index == 0 ? self.images.count - 1 : self.index - 1
            self.delegate?.imageCarouselView(self, didShowImageAt: self.index)
            self.previousImageView.image = self.index == 0 ? self.images[self.images.count - 1] : self.images[self.index - 1]

            self.setupLayout()
        })
    }

    private func showNextImage() {
        nextImageLeadingConstraint.constant = 0
        currentImageLeadingConstraint.constant = -screenWidth

        UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: {
            self.layoutIfNeeded()
        }, completion: { _ in
            self.previousImageView = self.currentImageView
            self.currentImageView = self.nextImageView
            self.nextImageView = self.imageView(image: nil, contentMode: .scaleAspectFit)

            self.index = self.index == (self.images.count - 1) ? 0 : self.index + 1
            self.delegate?.imageCarouselView(self, didShowImageAt: self.index)
            self.nextImageView.image = self.index == (self.images.count - 1) ? self.images[0] : self.images[self.index + 1]

            self.setupLayout()
        })
    }

    func imageView(image: UIImage? = nil, contentMode: UIImageView.ContentMode) -> UIImageView {
        let view = UIImageView()

        view.image = image
        view.contentMode = contentMode
        view.translatesAutoresizingMaskIntoConstraints = false

        return view
    }
}

// ImageCarouselViewDelegate

import UIKit

protocol ImageCarouselViewDelegate: NSObjectProtocol {
    func imageCarouselView(_ imageCarouselView: ImageCarouselView, didShowImageAt index: Int)
}

// Usage

let slideshowView = ImageCarouselView(images) // initialize

self.slideshowView.delegate = self // set delegate in viewDidLoad()

extension YourViewController: ImageCarouselViewDelegate {
    func imageCarouselView(_ imageCarouselView: ImageCarouselView, didShowImageAt index: Int) {
        // do something with index
    }
}

【讨论】:

    猜你喜欢
    • 2014-03-21
    • 2017-11-26
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    • 2020-10-12
    • 2017-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多