利用UICollectionView实现新特性启动页。没有新版本的情况下,程序运行展示默认启动图。附带跳过默认启动图。
首次运行或有新版本时
2017-01-11 13_52_45.gifimport UIKit
private let reuseIdentifier = "Cell"
class FcNewfeaturesCollectionVC: UICollectionViewController {
fileprivate let pageCount = 4
fileprivate var layout:UICollectionViewFlowLayout = NewfeaturesLayout()
/** 指示器 */
fileprivate lazy var page: UIPageControl = {
let page = UIPageControl()
page.pageIndicatorTintColor = UIColor.lightGray
page.currentPageIndicatorTintColor = UIColor.red
page.isUserInteractionEnabled = false
return page
}()
// 初始化
init() {
super.init(collectionViewLayout: layout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.view?.addSubview(self.page)
self.page.numberOfPages = self.pageCount
self.page.frame = CGRect(x: (UIScreen.main.bounds.size.width - 100) / 2, y: UIScreen.main.bounds.size.height - 80, width: 100, height: 10)
self.collectionView!.register(FcNewfeaturesCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
// MARK: UICollectionViewDataSource
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pageCount
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! FcNewfeaturesCell
cell.imageIndex = indexPath.item
return cell
}
// MARK: UICollectionViewDelegate
// 完全显示cell之后调用
override func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// 1.拿到当前显示的cell对应的索引
let path = collectionView.indexPathsForVisibleItems.last!
// 2、判断是否是最后一个cell
if path.item == (pageCount - 1) {
// 拿到当前索引对应的cell
let cell = collectionView.cellForItem(at: path) as! FcNewfeaturesCell
// 让cell执行按钮动画
cell.startAnimation()
}
}
// 监听collectionView的滚到
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 1、获取滚动的偏移量 + scrollView.bounds.width * 0.5给偏移量加一半,当滑动一般就滚动pageControl的当前选中
let offsetX = scrollView.contentOffset.x + scrollView.bounds.width * 0.5
// 2、计算pageContra的currentIndex
page.currentPage = Int(offsetX / scrollView.bounds.width) % self.pageCount
}
}
// MARK - Cell
class FcNewfeaturesCell: UICollectionViewCell {
/** icon */
fileprivate lazy var iconView = UIImageView()
/** 进入按钮 */
fileprivate lazy var startButton: UIButton = {
let button = UIButton()
button.setBackgroundImage(UIImage(named: "new_feature_button"), for: .normal)
button.setBackgroundImage(UIImage(named: "new_feature_button_highlighted"), for: .highlighted)
button.isHidden = true
button.addTarget(self, action:#selector(butClick), for: .touchUpInside)
return button
}()
/** 保存图片的索引 */
fileprivate var imageIndex:Int = 0 {
didSet{
iconView.image = UIImage(named: "new_feature_\(imageIndex + 1)")
startButton.isHidden = true
}
}
// MARK - 系统回调
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/** 设置UI */
fileprivate func setupUI(){
// 1.添加子控件到CollectionView上
contentView.addSubview(iconView)
iconView.frame = contentView.bounds
// 2、在最后一页添加 “进入按钮”
contentView.addSubview(startButton)
startButton.frame = CGRect(x: (UIScreen.main.bounds.size.width - 150) / 2, y: 500, width: 150, height: 40)
}
/** 进入按钮点击事件 */
func butClick(){
// 发送通知
let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
NotificationCenter.default.post(name: notifyChat, object: nil)
}
/** 让按钮开始动画*/
func startAnimation(){
startButton.isHidden = false
// 执行动画
startButton.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
startButton.isUserInteractionEnabled = false
// UIViewAnimationOptions(rawValue: 0) 等于 OC中 knilOptions
UIView.animate(withDuration: 1.2, delay: 0.0, usingSpringWithDamping: 1.2, initialSpringVelocity: 6, options: UIViewAnimationOptions(rawValue: 0), animations: { () -> Void in
// 还原,清空形变
self.startButton.transform = CGAffineTransform.identity
}, completion: { (_) -> Void in
self.startButton.isUserInteractionEnabled = true
})
}
}
// MARK - Layout
class NewfeaturesLayout: UICollectionViewFlowLayout {
override func prepare() {
// 1.设置layout布局
itemSize = UIScreen.main.bounds.size
minimumInteritemSpacing = 0
minimumLineSpacing = 0
scrollDirection = .horizontal
// 2.设置CollectionView的属性
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.bounces = false
collectionView?.isPagingEnabled = true
}
}
无新特性展示默认启动图,自动跳转主页
1、普通倒计时跳过
2017-01-11 12_58_16.gif
import UIKit
/** 倒计时总时间 */
private let kCountdownTime:Int = 5
class FcNormalWecomVC: UIViewController {
// MARK - 懒加载区
/** 背景容器 */
fileprivate lazy var bgView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.white
return view
}()
/** 启动图 */
fileprivate lazy var iconImageView: UIImageView = {
let icon = UIImageView()
icon.image = UIImage(named: "123")
return icon
}()
/** 跳过按钮 */
fileprivate lazy var skipButton: UIButton = {
let skipBut = UIButton()
skipBut.layer.cornerRadius = 5
skipBut.layer.borderWidth = 1
skipBut.layer.borderColor = UIColor.red.cgColor
skipBut.frame = CGRect(x: self.view.frame.size.width * 0.78, y: 20, width: 80, height: 25)
skipBut.backgroundColor = UIColor.clear
skipBut.setTitle("跳过(5s)", for: .normal)
skipBut.setTitleColor(UIColor.red, for: .normal)
skipBut.addTarget(self, action: #selector(normalButClick), for: .touchUpInside)
return skipBut
}()
// MARK - 属性区
/** 剩余时间 */
var remainingTime: Int = 0 {
willSet{
skipButton.setTitle("跳过\(newValue)s", for: .normal)
if newValue <= 0 {
isCounting = false
}
}
}
/** 计时器 */
var timer: Timer?
/** 按钮开关 */
var isCounting:Bool = false {
willSet {
if newValue {
/** 初始化计时器 */
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
remainingTime = kCountdownTime
}else {
// 暂停且销毁计时器
timer?.invalidate()
timer = nil
}
}
}
// MARK - 系统回调
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 延迟5秒执行操作
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(kCountdownTime)) {
UIView .animate(withDuration: 0.6, animations: {
self.bgView.alpha = 0.3
self.bgView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
}, completion: { (bool) in
self.bgView.alpha = 0
let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
NotificationCenter.default.post(name: notifyChat, object: nil)
})
}
}
}
extension FcNormalWecomVC {
/** 设置UI */
func setupUI() {
view.backgroundColor = UIColor.white
view.addSubview(bgView)
self.bgView.frame = view.bounds
self.bgView.addSubview(iconImageView)
self.iconImageView.frame = self.bgView.bounds
self.bgView.addSubview(skipButton)
isCounting = true
}
}
extension FcNormalWecomVC {
/** 跳过按钮点击事件 */
func normalButClick(){
UIView .animate(withDuration: 0.6, animations: {
self.bgView.alpha = 0.3
self.bgView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
}, completion: { (bool) in
self.bgView.alpha = 0
// 发送通知
let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
NotificationCenter.default.post(name: notifyChat, object: nil)
})
}
/** 更新时间 */
func updateTime(timer: Timer){
remainingTime -= 1
}
}
2、带进度条倒计时跳过
2017-01-11 16_29_10.gif
import UIKit
/** 倒计时总时间 */
private let kCountdownTime:Int = 8
/** 线宽 */
private let kLineWidth:CGFloat = 2.0
class FcNormalWecomVC: UIViewController {
/** 带进度按钮计时器 */
var countView: CountDownView?
/** 背景容器 */
fileprivate lazy var bgView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.white
return view
}()
/** 启动图 */
fileprivate lazy var iconImageView: UIImageView = {
let icon = UIImageView()
icon.image = UIImage(named: "123")
return icon
}()
// MARK:- 系统回调
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 延迟5秒执行操作
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(kCountdownTime)) {
UIView .animate(withDuration: 0.6, animations: {
self.bgView.alpha = 0.3
self.bgView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
}, completion: { (bool) in
self.bgView.alpha = 0
let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
NotificationCenter.default.post(name: notifyChat, object: nil)
})
}
}
}
// MARK:- 设置UI
extension FcNormalWecomVC {
fileprivate func setupUI() {
view.backgroundColor = UIColor.white
view.addSubview(bgView)
self.bgView.frame = view.bounds
self.bgView.addSubview(iconImageView)
self.iconImageView.frame = self.bgView.bounds
// 跳过按钮
let progressBut = UIButton()
progressBut.frame = CGRect(x: self.view.frame.size.width - 50 - 35 , y: 20, width: 50, height: 25)
progressBut.titleLabel?.font = UIFont.systemFont(ofSize: 15)
progressBut.setTitleColor(UIColor.red, for: .normal)
progressBut.setTitle("跳 过", for: .normal)
progressBut.backgroundColor = UIColor.clear
progressBut.addTarget(self, action: #selector(progressButtonCilck), for: .touchUpInside)
self.bgView.addSubview(progressBut)
// 圈圈
let viewFrame = CGRect(x: self.view.frame.size.width - 35, y: 20, width: 25, height: 25)
let countDownView = CountDownView(frame: viewFrame, totalTime: kCountdownTime, lineWidth: kLineWidth, lineColor: UIColor.red, textFontSize: 2, startFinishCallback: {
progressBut.isHidden = false
}, completeFinishCallback: {
progressBut.isHidden = true
})
self.bgView.addSubview(countDownView)
// 开始倒计时
countDownView.startTheCountDown()
}
}
// MARK:- 事件处理
extension FcNormalWecomVC {
func progressButtonCilck(){
UIView .animate(withDuration: 0.6, animations: {
self.bgView.alpha = 0.3
self.bgView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
}, completion: { (bool) in
self.bgView.alpha = 0
// 发送通知
let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
NotificationCenter.default.post(name: notifyChat, object: nil)
})
}
}
(PS:带进度按倒计时涉及到另外一个自定义类,代码如下)
import UIKit
class CountDownView: UIView {
// MARK:- 属性定义
var startBack: (() -> ())?
var completeBack: (() -> ())?
var progressLineWidth: CGFloat?
var progressLineColor: UIColor?
var countLb: UILabel?
var totalTimes: NSInteger = 0
var isCountDown:Bool = false
// MARK:- 懒加载
fileprivate lazy var progressLayer:CAShapeLayer = {
let progress = CAShapeLayer()
let kWidth = self.frame.size.width
let kHeight = self.frame.size.height
progress.frame = CGRect(x: 0, y: 0, width: kWidth, height: kHeight)
progress.position = CGPoint(x: kWidth/2.0, y: kHeight/2.0)
let point = CGPoint(x: kWidth/2.0, y: kHeight/2.0)
progress.path = UIBezierPath(arcCenter: point, radius: (kWidth - self.progressLineWidth!)/2.0, startAngle: 0, endAngle: CGFloat(M_PI * 2), clockwise: true).cgPath
progress.fillColor = UIColor.clear.cgColor
progress.lineWidth = self.progressLineWidth!
progress.strokeColor = self.progressLineColor!.cgColor
progress.strokeEnd = 0
progress.strokeStart = 0
progress.lineCap = kCALineCapRound
return progress
}()
fileprivate lazy var rotateAnimation:CABasicAnimation = {
let rotateAnima = CABasicAnimation(keyPath: "transform.rotation.z")
rotateAnima.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
rotateAnima.fromValue = CGFloat(2 * M_PI)
rotateAnima.toValue = CGFloat(0)
rotateAnima.duration = CFTimeInterval(self.totalTimes)
rotateAnima.isRemovedOnCompletion = false
return rotateAnima
}()
fileprivate lazy var strokeAnimationEnd:CABasicAnimation = {
let strokeAnimation = CABasicAnimation(keyPath: "strokeEnd")
strokeAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
strokeAnimation.duration = CFTimeInterval(self.totalTimes)
strokeAnimation.fromValue = CGFloat(1)
strokeAnimation.toValue = CGFloat(0)
strokeAnimation.speed = Float(1.0)
strokeAnimation.isRemovedOnCompletion = false
return strokeAnimation
}()
fileprivate lazy var animationGroup:CAAnimationGroup = {
let group = CAAnimationGroup()
group.animations = [self.strokeAnimationEnd, self.rotateAnimation]
group.duration = CFTimeInterval(self.totalTimes)
return group
}()
/**
* 初始化定时器
frame 位置
totalTime 总时间
lineWidth 线宽
lineColor 线色
fontSize 字体大小(控件的宽度/多少)
startFinishCallback 开始闭包
completeFinishCallback 完成闭包
*/
init(frame: CGRect, totalTime: NSInteger, lineWidth:CGFloat, lineColor:UIColor, textFontSize:CGFloat, startFinishCallback:@escaping () -> (), completeFinishCallback:@escaping () -> ()) {
super.init(frame: frame)
startBack = startFinishCallback
completeBack = completeFinishCallback
progressLineWidth = lineWidth
progressLineColor = lineColor
totalTimes = totalTime
layer.addSublayer(progressLayer)
createLabel(textFontSize: textFontSize)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK:- 逻辑处理
extension CountDownView {
/** 创建label */
fileprivate func createLabel(textFontSize: CGFloat) {
let label = UILabel()
label.frame = self.bounds
label.textColor = self.progressLineColor
label.font = UIFont.systemFont(ofSize: self.frame.size.width / textFontSize)
label.textAlignment = .center
addSubview(label)
countLb = label
}
/** 创建定时器 */
fileprivate func createTimer() {
weak var weakSelf = self
if totalTimes > 0 {
var timeout = totalTimes
// 1、获取一个全局队列
let queue = DispatchQueue.global()
// 2、创建间隔定时器
let time = DispatchSource.makeTimerSource(flags: [], queue: queue)
time.scheduleRepeating(deadline: .now(), interval: .seconds(1), leeway: .microseconds(100))
time.setEventHandler(handler: {
if timeout <= 0 {
// 2.1定时器取消
time.cancel()
// 2.2主线程刷新UI
DispatchQueue.main.async(execute: {
weakSelf?.isCountDown = false
weakSelf?.isHidden = true
weakSelf?.stopCountDown()
})
}else {
weakSelf?.isCountDown = true
let seconds = timeout % 60
DispatchQueue.main.async(execute: {
self.countLb?.text = "\(String(seconds))s"
})
timeout -= 1
}
})
// 3、复原定时器
time.resume()
}
}
/** 开始倒计时 */
func startTheCountDown(){
self.isHidden = false
isCountDown = true
if (startBack != nil) {
startBack!()
}
createTimer()
progressLayer.add(animationGroup, forKey: "group")
}
/** 停止倒计时 */
fileprivate func stopCountDown(){
progressLayer.removeAllAnimations()
if completeBack != nil {
completeBack!()
}
}
}
普通、带进度条按钮都支持点击 跳过 进行跳转
2017-01-11 12_58_59.gifAPPdelegate中代码
import UIKit
// 切换控制器通知
let FcSwitchRootViewControllerKey = "FcSwitchRootViewControllerKey"
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// 接收通知
NotificationCenter.default.addObserver(self, selector: #selector(switchRootViewController(notify:)), name: NSNotification.Name( FcSwitchRootViewControllerKey), object: nil)
window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = UIColor.white
window?.rootViewController = defaultController()
window?.makeKeyAndVisible()
isNewUpdate()
return true
}
/** 实现通知方法 */
func switchRootViewController(notify: NSNotification) {
window?.rootViewController = ViewController()
}
/** 销毁通知 */
deinit {
NotificationCenter.default.removeObserver(self)
}
/** 用于获取默认显示界面 */
fileprivate func defaultController() -> UIViewController {
return isNewUpdate() ? FcNewfeaturesCollectionVC() : FcNormalWecomVC()
}
/** 检查是否有新版本 */
// 此修饰词作用为消除 "Result of call to ‘isNewUpdate()’is unused" 没有使用返回结果的警告。
@discardableResult
fileprivate func isNewUpdate() -> Bool {
let key = "CFBundleShortVersionString"
// 1.获取应用程序 -> 当前版本 info.plist
let currentVersion = Bundle.main.infoDictionary![key] as! String
// print("当前版本 -> \(currentVersion)")
// 2.获取沙河应用程序版本 -> 从本地文件读取(以前自己存储) String ?? "0.0" 如果String是空,则取 0.0
let sandboxVersion = UserDefaults.standard.value(forKey: key) as? String ?? "0.0"
// print("老版本 -> \(sandboxVersion)")
// 3.利用当前版本 和 以前版本比较
// 2.0 1.0 降序
if currentVersion.compare(sandboxVersion) == ComparisonResult.orderedDescending{
// 3.1如果有新版本, 将新版本保存
let defaults = UserDefaults.standard
defaults.setValue(currentVersion, forKey: key)
// iOS 7.0 之后,就不需要同步了,iOS 6.0 之前,如果不同步不会第一时间写入沙盒
defaults.synchronize()
// 4.返回时候有新版本
return true
}
return false
}
网友评论