UIPageControl 中的控件 "active" 点
Control "active" dot in UIPageControl
我的设计师要求我在 UIPageViewController 中显示 3 个点以显示 10 个视图。
当前3个视图控制器显示时,第0个点应该高亮显示;当接下来的 4 个视图控制器显示时,第一个点应该突出显示;当最后 3 个视图控制器显示时,第二个点应该突出显示。
到目前为止,我能够在 UIPageControl 中显示 3 个点,但指示器点只是旋转,指示 n%3
位置处于活动状态。
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return 3
}
我还没有看到任何关于如何使用 UIPageControl 进行控制的文档,索引是活动索引,所以我不确定这是否是 Apple 希望您能够覆盖的内容。
如果有办法完成我想做的事情,我将不胜感激。
原来我想要完成的事情不能用 UIPageViewController 来完成。默认情况下,此 class 中的 UIPageControl 无法直接覆盖。
相反,我能够结合使用 UICollectionView(带有允许它在页面更改效果中类似于 UIPageViewController 的 hack)和 UIPageControl,作为同一个总体 UIViewController 的子视图。
class MyPageViewController : UIViewController {
// MARK: subviews
private var collectionView:UICollectionView!
/// the collection layout controls the scrolling behavior of the collection view
private var collectionLayout = MyLayout()
private var pageControl = UIPageControl()
let CollectionViewCellReuseIdentifer = "CollectionViewCellReuseIdentifier"
// MARK: autolayout
private var autolayoutConstraints:[NSLayoutConstraint] = [NSLayoutConstraint]()
// MARK: constructors
init() {
super.init(nibName: nil, bundle: nil)
}
// MARK: UIViewController lifecycle methods
override func viewDidLoad() {
super.viewDidLoad()
self.setupView()
}
/**
Set up the collection view, page control, skip & log in buttons
*/
func setupView() {
self.setupCollectionView()
self.setupPageControl()
self.setupConstraints()
self.view.addConstraints(self.autolayoutConstraints)
}
/**
Set up the collection view
*/
func setupCollectionView() {
self.collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: self.collectionLayout)
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
self.collectionView.registerClass(MyPageView.self, forCellWithReuseIdentifier: self.CollectionViewCellReuseIdentifer)
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.backgroundColor = UIColor.whiteColor()
self.collectionView.scrollEnabled = true
self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;
self.collectionLayout.minimumInteritemSpacing = 1
self.collectionLayout.minimumLineSpacing = 1
self.collectionLayout.scrollDirection = .Horizontal
self.collectionLayout.delegate = self
self.view.addSubview(self.collectionView)
}
/**
Set up view showing pagination dots for slideshow items
*/
func setupPageControl() {
self.pageControl.translatesAutoresizingMaskIntoConstraints = false
self.pageControl.numberOfPages = 3
self.pageControl.backgroundColor = UIColor.whiteColor()
self.view.addSubview(self.pageControl)
}
func setupConstraints() {
let views:[String:AnyObject] = [
"collectionView" : self.collectionView,
"pageControl" : self.pageControl,
]
self.autolayoutConstraints.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[collectionView][pageControl]|",
options: .AlignAllCenterX,
metrics: nil,
views: views
)
)
self.autolayoutConstraints.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[collectionView]|",
options: .AlignAllCenterY,
metrics: nil,
views: views
)
)
self.autolayoutConstraints.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[pageControl]|",
options: NSLayoutFormatOptions(),
metrics: nil,
views: views
)
)
}
}
extension MyPageViewController : MyPageViewControllerDelegate {
func didSwitchToPage(imageIndex: Int) {
if imageIndex < 3 {
self.pageControl.currentPage = 0
} else if imageIndex < 7 {
self.pageControl.currentPage = 1
} else {
self.pageControl.currentPage = 2
}
self.pageControl.setNeedsDisplay()
}
}
布局 class 源自我的同事在研究类似问题时找到的答案。 http://karmadust.com/centered-paging-with-preview-cells-on-uicollectionview/
/**
* Delegate for slide interactions
*/
protocol MyPageViewControllerDelegate {
/**
Triggered when a new page has been 'snapped' into place
- parameter imageIndex: index of the image that has been snapped to
*/
func didSwitchToPage(imageIndex: Int)
}
class MyLayout : UICollectionViewFlowLayout {
var delegate:MyPageViewControllerDelegate?
/*
Allows different items in the collection to 'snap' onto the current screen section.
Based off of http://karmadust.com/centered-paging-with-preview-cells-on-uicollectionview/
*/
override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
if let cv = self.collectionView {
let cvBounds = cv.bounds
let halfWidth = cvBounds.size.width * 0.5;
let proposedContentOffsetCenterX = proposedContentOffset.x + halfWidth;
if let attributesForVisibleCells = self.layoutAttributesForElementsInRect(cvBounds) {
var candidateAttributes : UICollectionViewLayoutAttributes?
// the index of the image selected
var index:Int = 0
for attributes in attributesForVisibleCells {
// == Skip comparison with non-cell items (headers and footers) == //
if attributes.representedElementCategory != UICollectionElementCategory.Cell {
index++
continue
}
if let candAttrs = candidateAttributes {
let a = attributes.center.x - proposedContentOffsetCenterX
let b = candAttrs.center.x - proposedContentOffsetCenterX
if fabsf(Float(a)) < fabsf(Float(b)) {
candidateAttributes = attributes;
}
}
else { // == First time in the loop == //
candidateAttributes = attributes;
index++
continue;
}
}
// Beautification step , I don't know why it works!
if(proposedContentOffset.x == -(cv.contentInset.left)) {
return proposedContentOffset
}
if let delegate = self.delegate {
delegate.didSwitchToPage((candidateAttributes?.indexPath.row)!)
}
return CGPoint(x: floor(candidateAttributes!.center.x - halfWidth), y: proposedContentOffset.y)
}
}
// fallback
return super.targetContentOffsetForProposedContentOffset(proposedContentOffset)
}
}
注意:我精简了我使用的实际代码并替换了一堆名称以使它们更适合示例。我没有 运行 这个特定的代码,也没有在我的 IDE 中测试错误。话虽这么说,代码背后的方法是可靠的。
我的设计师要求我在 UIPageViewController 中显示 3 个点以显示 10 个视图。
当前3个视图控制器显示时,第0个点应该高亮显示;当接下来的 4 个视图控制器显示时,第一个点应该突出显示;当最后 3 个视图控制器显示时,第二个点应该突出显示。
到目前为止,我能够在 UIPageControl 中显示 3 个点,但指示器点只是旋转,指示 n%3
位置处于活动状态。
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return 3
}
我还没有看到任何关于如何使用 UIPageControl 进行控制的文档,索引是活动索引,所以我不确定这是否是 Apple 希望您能够覆盖的内容。
如果有办法完成我想做的事情,我将不胜感激。
原来我想要完成的事情不能用 UIPageViewController 来完成。默认情况下,此 class 中的 UIPageControl 无法直接覆盖。
相反,我能够结合使用 UICollectionView(带有允许它在页面更改效果中类似于 UIPageViewController 的 hack)和 UIPageControl,作为同一个总体 UIViewController 的子视图。
class MyPageViewController : UIViewController {
// MARK: subviews
private var collectionView:UICollectionView!
/// the collection layout controls the scrolling behavior of the collection view
private var collectionLayout = MyLayout()
private var pageControl = UIPageControl()
let CollectionViewCellReuseIdentifer = "CollectionViewCellReuseIdentifier"
// MARK: autolayout
private var autolayoutConstraints:[NSLayoutConstraint] = [NSLayoutConstraint]()
// MARK: constructors
init() {
super.init(nibName: nil, bundle: nil)
}
// MARK: UIViewController lifecycle methods
override func viewDidLoad() {
super.viewDidLoad()
self.setupView()
}
/**
Set up the collection view, page control, skip & log in buttons
*/
func setupView() {
self.setupCollectionView()
self.setupPageControl()
self.setupConstraints()
self.view.addConstraints(self.autolayoutConstraints)
}
/**
Set up the collection view
*/
func setupCollectionView() {
self.collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: self.collectionLayout)
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
self.collectionView.registerClass(MyPageView.self, forCellWithReuseIdentifier: self.CollectionViewCellReuseIdentifer)
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.backgroundColor = UIColor.whiteColor()
self.collectionView.scrollEnabled = true
self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;
self.collectionLayout.minimumInteritemSpacing = 1
self.collectionLayout.minimumLineSpacing = 1
self.collectionLayout.scrollDirection = .Horizontal
self.collectionLayout.delegate = self
self.view.addSubview(self.collectionView)
}
/**
Set up view showing pagination dots for slideshow items
*/
func setupPageControl() {
self.pageControl.translatesAutoresizingMaskIntoConstraints = false
self.pageControl.numberOfPages = 3
self.pageControl.backgroundColor = UIColor.whiteColor()
self.view.addSubview(self.pageControl)
}
func setupConstraints() {
let views:[String:AnyObject] = [
"collectionView" : self.collectionView,
"pageControl" : self.pageControl,
]
self.autolayoutConstraints.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[collectionView][pageControl]|",
options: .AlignAllCenterX,
metrics: nil,
views: views
)
)
self.autolayoutConstraints.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[collectionView]|",
options: .AlignAllCenterY,
metrics: nil,
views: views
)
)
self.autolayoutConstraints.appendContentsOf(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[pageControl]|",
options: NSLayoutFormatOptions(),
metrics: nil,
views: views
)
)
}
}
extension MyPageViewController : MyPageViewControllerDelegate {
func didSwitchToPage(imageIndex: Int) {
if imageIndex < 3 {
self.pageControl.currentPage = 0
} else if imageIndex < 7 {
self.pageControl.currentPage = 1
} else {
self.pageControl.currentPage = 2
}
self.pageControl.setNeedsDisplay()
}
}
布局 class 源自我的同事在研究类似问题时找到的答案。 http://karmadust.com/centered-paging-with-preview-cells-on-uicollectionview/
/**
* Delegate for slide interactions
*/
protocol MyPageViewControllerDelegate {
/**
Triggered when a new page has been 'snapped' into place
- parameter imageIndex: index of the image that has been snapped to
*/
func didSwitchToPage(imageIndex: Int)
}
class MyLayout : UICollectionViewFlowLayout {
var delegate:MyPageViewControllerDelegate?
/*
Allows different items in the collection to 'snap' onto the current screen section.
Based off of http://karmadust.com/centered-paging-with-preview-cells-on-uicollectionview/
*/
override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
if let cv = self.collectionView {
let cvBounds = cv.bounds
let halfWidth = cvBounds.size.width * 0.5;
let proposedContentOffsetCenterX = proposedContentOffset.x + halfWidth;
if let attributesForVisibleCells = self.layoutAttributesForElementsInRect(cvBounds) {
var candidateAttributes : UICollectionViewLayoutAttributes?
// the index of the image selected
var index:Int = 0
for attributes in attributesForVisibleCells {
// == Skip comparison with non-cell items (headers and footers) == //
if attributes.representedElementCategory != UICollectionElementCategory.Cell {
index++
continue
}
if let candAttrs = candidateAttributes {
let a = attributes.center.x - proposedContentOffsetCenterX
let b = candAttrs.center.x - proposedContentOffsetCenterX
if fabsf(Float(a)) < fabsf(Float(b)) {
candidateAttributes = attributes;
}
}
else { // == First time in the loop == //
candidateAttributes = attributes;
index++
continue;
}
}
// Beautification step , I don't know why it works!
if(proposedContentOffset.x == -(cv.contentInset.left)) {
return proposedContentOffset
}
if let delegate = self.delegate {
delegate.didSwitchToPage((candidateAttributes?.indexPath.row)!)
}
return CGPoint(x: floor(candidateAttributes!.center.x - halfWidth), y: proposedContentOffset.y)
}
}
// fallback
return super.targetContentOffsetForProposedContentOffset(proposedContentOffset)
}
}
注意:我精简了我使用的实际代码并替换了一堆名称以使它们更适合示例。我没有 运行 这个特定的代码,也没有在我的 IDE 中测试错误。话虽这么说,代码背后的方法是可靠的。