Swift-高仿半糖App
来自: http://www.jianshu.com/p/7b57eb0c4abe
IOS-Swift2.0 高仿半糖App
写在前面的话
--少年我是去年毕业做IOS开发的,这个项目大概是2016年1月份到现在做完的,项目刚开始没学过Swift语言,但想着不会就该去挑战,于是边学习边做这个项目,现在对Swift也有了一定的了解
,在此感谢维尼的小熊的指导
关于项目(GitHub地址在文章最下方)
--这个开源项目为半糖, 官网➡ ,类似于美丽说,一款电商App,使用语言:Swift2.0,开发工具: Xcode 7.1,纯代码开发,耗时两个多月,数据为本地数据,用Charles进行抓包。
--项目测试机为5S,UI没有对6/6S进行适配
--因为开发时间有限,APP里面的各种分类相当繁多,因此首页中测试的话点第一个分类,第一个cell,一般都是有数据的,没有也会提示。
--我是个新手,Bug是难免的嘛。如果发现有Bug和疑问,请向我联系,QQ、简书、微博都可以
--Tips:首页支持3D Touch,可以试试哦~
项目效果图
首页展示
首页轮播图加展示.gif
首页-清单展示
首页-清单展示.gif
首页-搜索分类展示
首页-搜索展示.gif
3D Touch展示(用6S及6S以上测试)
3DTouch展示.gif
广场展示
首页-广场展示.gif
秀
消息(这一部分:joy:,我没好友,没粉丝,抓包木有数据,所以就按照自己的想法做咯)
个人中心(换头像请用真机测试~)
项目详细讲解 (按APP的启动顺序来)
启动页面
主要代码如下:
1.在appDelegate中
self.window?.rootViewController = mainViewController()//MARK: App首次启动显示 app的简介 func mainViewController() -> UIViewController{ //firstStart不为空,不是是第一次启动 if NSUserDefaults.standardUserDefaults().objectForKey("FirstStart") != nil { return self.tabbarController }else { //是第一次启动 NSUserDefaults.standardUserDefaults().setObject(false, forKey: "FirstStart") let firstVC = FirstStartViewController() return firstVC } }
Tips: 用户第一次启动App的时候 self.window.rootViewController = FirstStartViewController() 使用户进入引导页,动画完成后,点击『开启App之旅』,引导页发出 通知,appDelegate接受通知,再将 self.window?.rootViewController = self.tabBarController
首页-展示
①最好的设计思路应该是这样的:
tmp4c68a801.png
Tips:以下的思路 在个人中心很完整,前去看看哈
1.红色部分设计为 collectionHeaderView
方法: 初始化collecitonView时 在其layout参数中设置
layout.headerReferenceSize = CGSizeMake(width, height)然后collectionView 注册这个headerView
public func registerClass(viewClass: AnyClass?, forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String)` 然后 在collectionView代理方法 `func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView { var reuseView = UICollectionReusableView() if kind == UICollectionElementKindSectionHeader { let headView = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "collectionViewHeaderView", forIndexPath: indexPath) headView.addSubview(collectionHeadView) reuseView = headView } return reuseView }
2.绿色部分为collectionView 只作为容器使用
3.蓝色部分为显示的tableview,添加到collectionView的cell.contentView进行显示
Tips:现在很多App的启动页面也是这个思路,整体为一个UICollectionView,每一个cell都添加UITableView进行显示
②关于cell 动态高度的问题,上图:
tmp3734bf84.png
蓝色的这一部分是清单详情中的描述内容,JSON中的描述文字都不一样,所以导致整个cell的高度都是动态不定的
Tips:
1.在cell所有的model中添加 cellHeight 字段,在tableView代理方法 heightForRowAtIndexPath 中,判断model中的 cellHeight 字段是不是为空,有值则返回该值,类似
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { //model为对应cell的model if model.cellHeight != nil { return model.cellHeight } return 30 }
2.cell中的属性model 重写set方法,类似
var model: YourModel{ didSet{ //-------- 做你的操作 //------- if model.cellHeight == nil { model.cellHeight = 你计算后的高度 } } }
③ 3D Touch
Tips:其实App中应用最多的也就是不用点进去看。可以预览下一级显示的内容,比如 微信
3D Touch通用.gif
上代码:
viewDidLoad() 中 self.add3DTouch()
//MARK: 添加 3D touch功能 func add3DTouch() { //1.检测 3D touch 是否可用 if traitCollection.forceTouchCapability == .Available { //3DTouch可用, registerForPreviewingWithDelegate(self, sourceView: view) }else { //不可用 TipView.showMessage("不支持3Dtouch,换个6S吧,不谢:joy:") } }
Tips:
1 registerForPreviewingWithDelegate(delegate: UIViewControllerPreviewingDelegate, sourceView: UIView) 方法中delegate即为当前控制器self, sourceView 即为感应3DTouch功能的View,当前控制器中,我选择的是 self.view2.控制器上添加代理 UIViewControllerPreviewingDelegate ,实现方 func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? 和 func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) ,
看代码:
①这一部分其实就两个步骤
- 1.给3D Touch 你要预览的ViewController
- 2.设定3D Touch预览控制器的尺寸大小
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { //-----这一过程这么繁琐 说白了就是找到当前手指点到的 tableviewCell,后面 3Dtouch 预览的时候会用到 原尺寸 cell.frame let index = Int( showCollectionView.contentOffset.x / SCREEN_WIDTH ) let collectionViewCell = showCollectionView.cellForItemAtIndexPath(NSIndexPath(forRow: 0, inSection: index)) var tableView = UITableView() for aview in collectionViewCell!.contentView.subviews { if aview.isKindOfClass(UITableView.self) { tableView = aview as! UITableView } } //******* let indexPath = tableView.indexPathForRowAtPoint(location) let cell = tableView.cellForRowAtIndexPath(indexPath!) as! HomeCell let detailListContrller = ListDetailViewController(listId: "1872",transImage: cell.imgView.image!) //预显示的尺寸 detailListContrller.preferredContentSize = CGSizeMake(SCREEN_WIDTH, 600) //源尺寸 previewingContext.sourceRect = cell.frame return detailListContrller }
②对即将预览的ViewController,做一些操作,比如 隐藏tabbar,隐藏导航栏 等等,然后显示出来
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) { showViewController(viewControllerToCommit, sender: self) }
广场-展示
设计思路如图:
tmp4c3452b2.png
与首页相似,红色部分View是蓝色 mainCollectionView的headerView,红色View中又包含着一个collectionView
下拉刷新控件:
下拉刷新控件.gif
Tips:下拉刷新控件 使用SVPullToRefresh 框架,然后在它的文件中修改了一个方法,把自己想要出现的动图加了进去
- (SVPullToRefreshArrow *)arrow { if(!_arrow) { _arrow = [[SVPullToRefreshArrow alloc]initWithFrame:CGRectMake(0, self.bounds.size.height-54, 22, 48)]; _arrow.backgroundColor = [UIColor clearColor]; //将框架自带的刷新箭头屏蔽掉 // [self addSubview:_arrow]; //这一部分是我自定义显示的View UIImageView *gifImgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height-54, 125, 125)]; self.clipsToBounds = YES; gifImgView.backgroundColor = [UIColor whiteColor]; gifImgView.image = [UIImage sd_animatedGIFNamed:@"refreshGif"]; //设置刷新动图只在Loading状态下显示 [self setCustomView:gifImgView forState:SVPullToRefreshStateLoading]; } return _arrow; }
秀出自我
这一部分的实现思路比较简单
1.在appdelegate中,实现tabbarControllerDelegate方法, shouldSelectViewController 中,判断每次点击的是不是ShowMeController,如果是,则返回false,然后在当前的控制器 viewController.presentViewController(ShowMeViewController, animated: true, completion: nil)
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool { let childArray = tabbarController.childViewControllers let index = childArray.indexOf(viewController) if index == 2 { print("Show me!") presentShowMeViewController(viewController) return false }else if index == 3 { //点击 '消息中心' 延迟5s 后发出通知 //模拟网络刷新 viewController.tabBarItem.title = nil postNotificationCenter(tabbarController.viewControllers!) } return true }
2.对于调用照相机的方法,首先判断有没有摄像头
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) { imagePicker.sourceType = UIImagePickerControllerSourceType.Camera presentViewController(imagePicker, animated: true, completion: nil) }else { TipView.showMessage("骚年,找个有摄像头的手机吧。。") }
然后viewController中添加代理 UIImagePickerControllerDelegate,UINavigationControllerDelegate
在代理方法中可以获取到刚拍好的图片进行处理
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) { dispatch_async(dispatch_get_main_queue()) { () -> Void in picker.dismissViewControllerAnimated(true, completion: nil) self.topView.headerImage = image } }
消息这一部分就很少东西了,因为我没有粉丝,没有消息:joy:,
为了模拟后台发送消息,在tabBarController的"shouldSelectViewController"中,当点击该控制器时,appdelegate发出通知,MessageViewController中接收通知,提示用户收到消息
//接受 模拟后台的推送、、、、 NSNotificationCenter.defaultCenter().addObserver(self, selector: "hasNewMessage", name: UserHasNewMessage, object: nil)收到通知以后执行的方法:
func hasNewMessage(){ print("收到通知了。") TipView.showMessage("您有新的消息。但是你看不见:stuck_out_tongue_winking_eye:") }
个人中心
这一部分设计思路与广场类似,也都是上面是HeaderView,下面是UICollectionView,不同的是个人中心最上面有一个背景图片,会随着collectionView的contentOffSet变化而放大
设计思路:
tmp2ae3bf03.png
最后在 func scrollViewDidScroll(scrollView: UIScrollView) 中调整 backImageView的 transform 属性即可
2.关于停靠问题,
上图
tmp19a4b2b9.png
,姑且叫它 titleView , titleView滚到设定位置时会停靠到导航栏底下,思路如下:
-
titleView是添加到headerView中的,在 scrollViewDidScroll 方法中,监听collectionView的 contentOffset.y 的变化,当titleView到达设定的位置时, titleView.removeFromSuperview() ,然后计算frame,将其添加到View上,这样就做除了停靠的效果,这样做比较简单
:disappointed_relieved:: 刚开始我单独将titleView添加到view上,collectionView滑动时还得一直更改 titleView.frame
聊聊感想哈
其实从OC转向Swift非常简单,这个项目不难,很适合从OC转向swift,或swift初学者。
少年也是第一次发布自己的项目,忘老鸟轻拍,新手共勉,有什么问题或bug联系我哈,欢迎来 我的GitHub 上赏个Star:star2:
GitHub 代码下载地址
tmp1cbb03b9.png