DNSPageView
DNSPageView 是一个纯 Swift 的轻量级、灵活且易于使用的 PageView
框架,titleView
和 contentView
可以布局在任意地方,既可以纯代码初始化,也可以使用 xib
或者 Storyboard
初始化,并提供了一些常见的样式属性进行设置。
如果您使用的开发语言是 Objective-C,请使用 DNSPageView-ObjC
特性
- 使用简单
- 多种初始化方式
- 灵活布局
- 常见的样式
- 双击
titleView
的回调 -
contentView
滑动监听 - 适配 iOS 13 暗黑模式
- 动态改变样式
- 适配 RTL 布局
要求
- iOS 9.0+
- Xcode 10.2+
- Swift 5.0+
安装
CocoaPods
CocoaPods 是 Cocoa 项目的依赖管理器。您可以使用以下命令安装它
$ gem install cocoapods
CocoaPods 1.1+ 是构建 DNSPageView 所必需的。
要使用 CocoaPods 将 DNSPageView 集成到您的 Xcode 项目中,请在您的 Podfile
中指定它
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!
target '<Your Target Name>' do
pod 'DNSPageView'
end
然后,运行以下命令
$ pod install
手动
如果您不希望使用上述任何依赖管理器,您可以将 DNSPageView 手动集成到项目中。
示例
要运行示例项目,请克隆仓库并运行 DNSPageView.xcodeproj
。
使用
直接使用 PageView 初始化
// 创建 PageStyle,设置样式
let style = PageStyle()
style.isTitleViewScrollEnabled = true
style.isTitleScaleEnabled = true
// 设置标题内容
let titles = ["头条", "视频", "娱乐", "要问", "体育" , "科技" , "汽车" , "时尚" , "图片" , "游戏" , "房产"]
// 创建每一页对应的 controller
let childViewControllers: [UIViewController] = titles.map { _ -> UIViewController in
let controller = UIViewController()
addChild(controller)
return controller
}
let y = UIApplication.shared.statusBarFrame.height + (navigationController?.navigationBar.frame.height ?? 0)
let size = UIScreen.main.bounds.size
// 创建对应的 PageView,并设置它的 frame
// titleView 和 contentView 会连在一起
let pageView = PageView(frame: CGRect(x: 0, y: y, width: size.width, height: size.height - y), style: style, titles: titles, childViewControllers: childViewControllers)
view.addSubview(pageView)
使用 xib 或 storyboard 初始化
在 xib
或 storyboard
中拖出 2 个 UIView
,让它们分别继承 PageTitleView
和 PageContentView
,拖线到代码中
@IBOutlet weak var titleView: PageTitleView!
@IBOutlet weak var contentView: PageContentView!
对 PageTitleView 和 PageContentView 进行设置
// 创建 PageStyle,设置样式
let style = PageStyle()
style.titleViewBackgroundColor = UIColor.red
style.isShowCoverView = true
// 设置标题内容
let titles = ["头条", "视频", "娱乐", "要问", "体育"]
// 设置默认的起始位置
let startIndex = 2
// 创建每一页对应的 controller
let childViewControllers: [UIViewController] = titles.map { _ -> UIViewController in
let controller = UIViewController()
addChild(controller)
return controller
}
// 创建 PageViewManager 来设置它们的样式和布局
let pageViewManager = PageViewManager(style: style,
titles: titles,
childViewControllers: children,
currentIndex: currentIndex,
titleView: titleView,
contentView: contentView)
使用 PageViewManager 初始化
创建 PageViewManager
private lazy var pageViewManager: PageViewManager = {
// 创建 PageStyle,设置样式
let style = PageStyle()
style.isShowBottomLine = true
style.isTitleViewScrollEnabled = true
style.titleViewBackgroundColor = UIColor.clear
// 设置标题内容
let titles = ["头条", "视频", "娱乐", "要问", "体育"]
// 创建每一页对应的 controller
let childViewControllers: [UIViewController] = titles.map { _ -> UIViewController in
let controller = UIViewController()
addChild(controller)
return controller
}
return PageViewManager(style: style, titles: titles, childViewControllers: childViewControllers)
}()
布局 titleView 和 contentView
// 单独设置 titleView 的 frame
navigationItem.titleView = pageViewManager.titleView
pageViewManager.titleView.frame = CGRect(x: 0, y: 0, width: 180, height: 44)
// 单独设置 contentView 的大小和位置,可以使用 autolayout 或者 frame
let contentView = pageViewManager.contentView
view.addSubview(pageViewManager.contentView)
contentView.snp.makeConstraints { (maker) in
maker.edges.equalToSuperview()
}
样式
在 PageStyle
中提供了常见样式的属性,可以按照不同的需求进行设置,包括可以设置初始显示的页面
事件回调
DNSPageView 提供了常用事件监听回调,它属于 PageTitleViewDelegate
的可选属性
/// DNSPageView 的事件回调,如果有需要,请让对应的 childViewController 遵守这个协议
@objc public protocol PageEventHandleable: class {
/// 重复点击 pageTitleView 后调用
@objc optional func titleViewDidSelectSameTitle()
/// pageContentView 的上一页消失的时候,上一页对应的 controller 调用
@objc optional func contentViewDidDisappear()
/// pageContentView 滚动停止的时候,当前页对应的 controller 调用
@objc optional func contentViewDidEndScroll()
}
常见问题
-
style.isTitleViewScrollEnabled
如果
titles
的数量较少,建议设置style.isTitleViewScrollEnabled = false
,此时titleView
会固定,style.titleMargin
不起作用,每个title
平分整个titleView
的宽度,下划线的宽度等于title
的宽度。如果标签较多,建议设置
style.isTitleViewScrollEnabled = true
,此时titleView
会滑动,下划线的宽度会随着title
文字的宽度变化而变化 -
标签下划线的宽度跟随文字的宽度
设置
style.isTitleViewScrollEnabled = true
,可以参考Demo
中的第四种样式。 -
由于
DNSPageView
是基于UIScrollView
实现,因此也就不可避免地继承了它的某些特性:-
当控制器被
UINavigationController
管理,并且navigationBar.isTranslucent = true
时,当前控制器的view
是从y = 0
开始布局的,因此为了防止部分内容被navigationBar
遮挡,系统默认会给UIScrollView
添加 offset。如果你想取消这个特性:- iOS 11 以前,在控制器中设置
automaticallyAdjustsScrollViewInsets = false
- iOS 11 之后引入了
SafeArea
概念,设置UIScrollView
的属性contentInsetAdjustmentBehavior = .never
- 实际上这个效果还与
UIViewController
的其他属性有关,但由于各种组合的情景过于复杂,所以不在此一一描述
- iOS 11 以前,在控制器中设置
-
PageContentView
用UICollectionView
实现,因此这个特性有可能引发UICollectionView
的经典警告:The behavior of the UICollectionViewFlowLayout is not defined because。
the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values。
从而引发一些 Bug。
-
以上只是可能出现的 Bug 之一,由于
Demo
不能覆盖所有的场景,不同的布局需求可能会引起不同的 Bug,开发者需要明确了解自己的布局需求,注意细节,了解 iOS 布局特性,并且作出相应的调整,不能完全参照Demo
。
-
许可
DNSPageView 在 MIT 许可下可用。查看 LICENSE 文件以获取更多信息。