50天iOS挑战(Swift)
来源:互联网 发布:网络不良信息举报电话 编辑:程序博客网 时间:2024/04/29 13:09
50天iOS挑战(Swift) - 第10天:制作应用启动引导页面
50天,每天一个Swift语言的iOS练手项目,覆盖iOS开发的主要知识。贵在坚持,重在思考
文章列表:http://blog.csdn.net/b735098742/article/category/6978601
Github项目:https://github.com/Minecodecraft/50DaysOfSwift
简介
很多应用在用户初次启动时,会展示一个What’s new页面,如果打造一个多屏幕适配的启动界面,同时又保证低耦合性呢?Let’s do it!
本节将介绍启动界面的制作,下一节介绍登录界面的多屏幕适配。
由于代码较为复杂,文章中只讲制作过程,建议结合代码同步理解。
主要知识点: Xib设计模式、控制器解耦、代码重构、数据模型、KVC、KVO、CollectionView、PageControl、控件约束(屏幕适配)
竖屏模式:
横屏模式:
设计思路
1、设计架构
由于引导界面只是庞大项目的一个小模块,所以要尽可能减少耦合性。
本着各司其职的思路,我们将引导界面和登录界面分别放到两个故事版中。
引导界面需要用到的cell,采用Xib方式构建。(也可代码构建)
应用启动时,判断启动某个界面的工作,交给NavigationController。对于控制器间通讯,采用代码块内指针方式,以避免强引用循环(也可使用代理、闭方式避免)。
2、界面设计
由于要兼容横竖屏,所以界面约束的设计格外重要。
根据自己需求添加界面约束即可,在此不再赘述。
3、配置CollectionView
我们采用Xib构造,所以要在CollectionView注册Cell
collectionView.register(UINib.init(nibName: "PageViewCell", bundle: nil), forCellWithReuseIdentifier: guideCell)collectionView.register(UINib.init(nibName: "LoginViewCell", bundle: nil), forCellWithReuseIdentifier: loginCell)
3、建立数据模型
开发中我们与服务器通信时经常采用json,数据常以模型的形式存在。为了减少耦合性,我们采用数据模型来表示每一个引导页的内容。
苹果为我们提供了更简单的对属性赋值方式:KVC
var imageName: String?var title: String?var detail: String?init(dict: [String: Any]) { super.init() setValuesForKeys(dict)}override func setValue(_ value: Any?, forUndefinedKey key: String) { // Do something}
记着重写setValue:forUndefinedKey:,因为默认实现会抛出异常,程序crash
完成模型建立后,我们就可以通过键值对的方式对数据模型赋值。
4、最后一页隐藏按钮
从GIF中可以看到,翻到最后一页时我们要隐藏PageControl和两个按钮控件。
我们采用更改控件约束的方式
if off { pageControlBottom.constant = -20 skipButtonTop.constant = -50 continueButtonTop.constant = -50 // disable button to avoid crash skipButton.isEnabled = false continueButton.isEnabled = false } else { pageControlBottom.constant = 10 skipButtonTop.constant = 0 continueButtonTop.constant = 0 skipButton.isEnabled = true continueButton.isEnabled = true }
当然,这只是改变了其frame,系统并不一定立即重绘,我们调用layoutIfNeed方法进行重绘。
// layout subviews UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: { self.view.layoutIfNeeded() })
5、复用页面跳转方法
由于划屏和点击按钮都需要调用翻页方法,将翻页/页面跳转/按钮隐藏方法封装,提供接口供其他方法调用。
优化/Bug解决
1、横屏旋转时页面位置错误
因为Auto Layout Guide调整layout时,并不会调整对应的index,所以需要在willTransition中手动调用
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) { let indexPath = IndexPath(item: pageControl.currentPage, section: 0) self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true) }
但是这样还是没有解决,原因在于collectionView在旋转之前完成了scrollToItem方法。
解决方法很简单,提交一个延时0.01秒任务。
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) { let indexPath = IndexPath(item: pageControl.currentPage, section: 0) DispatchQueue.main.asyncAfter(deadline: .now()+0.01) { self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true) } }
2、解决导航控制器无rootViewController的问题
我们在APPDelegate中设置UIWindow为导航控制器,如下面代码
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. window = UIWindow(frame: UIScreen.main.bounds) window?.makeKeyAndVisible() let nvc = MainNavigationController() window?.rootViewController = nvc return true }
但是,当我们在导航控制器中初始化引导页面控制器/登录界面控制器/登陆成功界面控制器时,可能出现没有view没有成功present的情况。原因和步骤1很类似,将present任务延时提交即可。
如下面代码,导航控制器的初始化过程:
class MainNavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor.cyan // 添加delay时间,放置在window初始化之前显示 if isFirstRun() { perform(#selector(showGuideController), with: nil, afterDelay: 0.01) } else if isLoggedIn() { // show } else { perform(#selector(showLoginController), with: nil, afterDelay: 0.01) } } func isFirstRun() -> Bool { return true } func isLoggedIn() -> Bool { return false } @objc func showGuideController() { let storyboard = UIStoryboard(name: "Guide", bundle: nil) let guideVC = storyboard.instantiateInitialViewController() as! GuideViewController present(guideVC, animated: false) { // do something } } func finishShowGuideController() { dismiss(animated: false, completion: nil) showLoginController() } @objc func showLoginController() { let storyboard = UIStoryboard(name: "Login", bundle: nil) let loginVC = storyboard.instantiateInitialViewController() as! LoginViewController present(loginVC, animated: false) { // do something } }}
详细过程请见代码,下一节将介绍登录界面的屏幕适配问题,包括输入时屏幕上移、重绘输入框等功能实现。
项目源码
项目源码已传至Github:(https://github.com/Minecodecraft/50DaysOfSwift)
后续教程、代码、库持续更新,欢迎Star关注。希望可以对大家有所帮助
- 50天iOS挑战(Swift)
- 50天iOS挑战(Swift)
- 50天iOS挑战(Swift)
- 50天iOS挑战(Swift)
- 50天iOS挑战(Swift)
- 50天iOS挑战(Swift)
- 50天iOS挑战(Swift)
- iOS GameCenter 挑战,排名
- iOS GameCenter 挑战,排名
- iOS--swift
- iOS网络请求认证挑战
- ios(swift语言)学习第二天
- IOS(swift语言)第5天
- IOS(swift语言)第6天
- [iOS]#Swift#OC+Swift混编
- IOS-Swift简介
- [ios]Swift之?和!
- ios--swift完整教程
- gazebo
- [设计]状态者模式
- shell 数学计算
- cmake使用示例与整理总结
- IDEA配置github并上传项目
- 50天iOS挑战(Swift)
- 简易计算器 (C语言)作业
- 难点攻克-大整数运算
- 一维数组
- 分布式搜索之搭建Solrcloud(Solr集群)
- [设计]中介者模式
- 购物车删除按钮
- 经验总结-完整介绍Android Studio中Git的使用之git的基本准备及提交代码到本地(一)-1
- Angular5 开发之路