Swift-高仿半糖App

作者: ManoBoo | 来源:发表于2016-03-02 17:07 被阅读10068次

    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

    秀我展示.gif

    消息(这一部分😂,我没好友,没粉丝,抓包木有数据,所以就按照自己的想法做咯)

    消息.gif

    个人中心(换头像请用真机测试~)

    个人中心.gif

    项目详细讲解 (按APP的启动顺序来)

    启动页面

    启动页面.gif
    主要代码如下:
    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吧,不谢😂")    
            }
        }
    

    Tips:
    1 registerForPreviewingWithDelegate(delegate: UIViewControllerPreviewingDelegate, sourceView: UIView)方法中delegate即为当前控制器self,sourceView即为感应3DTouch功能的View,当前控制器中,我选择的是self.view
    2.控制器上添加代理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
            }
            
        }
    

    消息这一部分就很少东西了,因为我没有粉丝,没有消息😂,

    为了模拟后台发送消息,在tabBarController的"shouldSelectViewController"中,当点击该控制器时,appdelegate发出通知,MessageViewController中接收通知,提示用户收到消息
    //接受 模拟后台的推送、、、、 NSNotificationCenter.defaultCenter().addObserver(self, selector: "hasNewMessage", name: UserHasNewMessage, object: nil)

    收到通知以后执行的方法:

    func hasNewMessage(){
            print("收到通知了。")
            TipView.showMessage("您有新的消息。但是你看不见😜")
        }
    

    个人中心

    这一部分设计思路与广场类似,也都是上面是HeaderView,下面是UICollectionView,不同的是个人中心最上面有一个背景图片,会随着collectionView的contentOffSet变化而放大
    设计思路:


    tmp2ae3bf03.png

    1.黄色View为 UIImageView,处于整个View的下层,collectionView和它的headerView的backgroundColor = UIColor.clearColor()
    最后在func scrollViewDidScroll(scrollView: UIScrollView)中调整 backImageView的transform属性即可
    2.关于停靠问题,

    停靠问题.gif
    上图
    tmp19a4b2b9.png
    ,姑且叫它titleView, titleView滚到设定位置时会停靠到导航栏底下,思路如下:
    1. titleView是添加到headerView中的,在scrollViewDidScroll方法中,监听collectionView的contentOffset.y的变化,当titleView到达设定的位置时,titleView.removeFromSuperview(),然后计算frame,将其添加到View上,这样就做除了停靠的效果,这样做比较简单
      😥: 刚开始我单独将titleView添加到view上,collectionView滑动时还得一直更改titleView.frame

    聊聊感想哈

    其实从OC转向Swift非常简单,这个项目不难,很适合从OC转向swift,或swift初学者。
    少年也是第一次发布自己的项目,忘老鸟轻拍,新手共勉,有什么问题或bug联系我哈,欢迎来我的GitHub上赏个Star🌟

    GitHub 代码下载地址

    代码下载地址,给个star再走🙏
    直接打开运行工程

    tmp1cbb03b9.png

    收到有童鞋反应说,会遇到这样的Bug导致无法运行

    tmp788532b0.png

    Tips:由于GitHub上传的问题,解决办法如下:

    tmp44a0f1ff.png

    项目编写中用的切图、标注、及本地数据在我的百度云

    切图、数据下载地址-ManoBpp的百度云

    相关文章

      网友评论

      • 橘子香蕉我爱吃:大神。我也是刚开始学swift。看了看斯坦福大学iOS10开发。不太理解mv c
        ManoBoo:@刘小成_da7e 其实入门时不用了解具体的概念,多去写写代码,对这个概念会有更深入的了解
        橘子香蕉我爱吃:@ManoBoo 感谢:pray:
        ManoBoo:在MVC结构中,Model 即为模型, View即 视图层, Controller即为逻辑层
        分别对应着项目中的 Model模型,比如用户类, View对应的就是各个视图, Controller 对应的是 ViewController,处理业务逻辑相关
      • 咔客:代码 为啥不更新啊 ?都报错 3.0
        ManoBoo:@咔客 :smile: 这个是swift2.0 的项目,最近在写swift3.1的开源项目,欢迎持续关注
      • d1083172fea9:可以写的不错
      • 小小Elle:进入app就出现了这个错误:unexpectedly found nil while unwrapping an Optional value
        ManoBoo:@小小Elle 那条语句可以直接屏蔽的
        ManoBoo:@小小Elle 这个是选择图片那个页面 有一个print输出语句中的错误
        小小Elle:刚刚又运行了一下,就灭有这个错误了,这是为什么呢?
      • 大龄菜鸟程序员:有个应用叫做凹凸曼,里面有个功能可以实现输入淘宝分享连接后,自动弹出图片,标题,和价格显示。我想请教个思路是怎么做的,sdk?解析html?
        ManoBoo:@大龄菜鸟程序员 这个不太清楚,不过可能有两种,一种是有淘宝的接口,跟淘宝合作,另外一种就是解析HTML
      • 尼古拉斯赵四爷:感谢楼主分享
      • 虚心若愚:请教一下,你是怎么快速从OC 转换成swift 开发的?
        ManoBoo:@虚心若愚 其实我觉得差不多,可能就是一些写法上的变化,整体的逻辑基本没有变化,block变成了闭包,还有一些? !之类的区别,都是在用的时候去查了查
      • 闲鱼尼克:已给✨
        ManoBoo:@b849aa1550ea 哈哈,你之前学过OC,那转向swift基本没有难度的:smile:
        ManoBoo:@b849aa1550ea :smiley: thanks
        闲鱼尼克:之前都是用OC 现在闲下来了 想学习一下 swift 请大哥带我飞
      • fda524dc01f6:学习了
      • b5f05d9357e2:请问首页tableview向上滚动的时候,header也同时向上原理是怎么实现的,我做类似功能的时候head不动,tableview在下面滚
        ManoBoo:@袁克强 我是这么做 当标签栏(最新 文艺。。)滚动到一定位置的时候,将这个view remove掉 然后添加到self.view上 就可以了 然后向下滚动的时候再添加回去
        b5f05d9357e2:@ManoBoo 也是像半糖那样,下面是tab,有多个tableview, 监听tableview的contentOffset变化我知道,但是header向上的同时,tableview也向上滚动了一部分了,导致tableview界面部分被tab(最新,文艺,礼物那个view)挡住了。 你是如何做到tableview不被挡住,直到滚动到最上tab不动,tableview才被挡住。。
        ManoBoo:@袁克强 如果只有 tableview + header的 话 就把header 作为tableview的 tableHeaderView ,如果是其他的话 在scrollViewDidScroll 方法里面 监听tableview的contentOffset变化 然后更改header的frame
      • SwiftYang:作者这个高仿半糖以及那个 高仿爱鲜蜂 被某些人拿去我朋友公司面试,开价12K,可惜没能蒙混过关,随便问他几个开发常遇到的问题,然后就没然后了
        ManoBoo:@SwiftYang :smile:有什么问题一起交流哈
        SwiftYang:@ManoBoo 我想做小熊的朋友 :joy: ,也想做你的朋友,已粉你们俩 :smile:
        ManoBoo:@SwiftYang 你是小熊的朋友嘛 :joy:
      • 超_iOS:swift 啊,不是太看得懂
        ManoBoo:大体都一样的
      • 醉看红尘这场梦:大师!你怎么学的这么快?
        醉看红尘这场梦:@ManoBoo 维尼的小熊怎么不留联系方式的。
        醉看红尘这场梦:@ManoBoo 我技术不行,才一年。学的又慢
        ManoBoo:@醉看红尘这场梦 ……其实没那么夸张,一方面喜欢遨游在代码的世界,另一方面工作压力咯
      • InLefter:不错不错~~前来学习哈
      • TongRy:以前的文章都是oc,swift是才学的吗?给点经验呗,最近貌似学着有点困难了
        ManoBoo:@TongRy 我,基本都百度,然后自己多试了试,
        TongRy:@ManoBoo 对,前几天还经常迷糊,你看的什么教程?我还在看官方中文版
        ManoBoo:@TongRy 嗯,我觉得吧,我刚开始遇到最多的坑就是?和!
        还有就是闭包的使用
      • 阿龙欧巴:正在看维尼小熊的爱鲜蜂,之后也研读下大神你的代码,觉得你们都好强啊!希望我会有点sence,也做出点啥玩意出来
        阿拉斯加的狗:@ManoBoo 大牛哎
        ManoBoo:@阿龙欧巴 小熊是真大神,我还不是😄
      • 漂移爱转角:大神,我是刚学iOS开发不久,不知道什么时候到你这个程度,给个微信,以后多多指教。
        ManoBoo:@angelorlover 😁我也不是大神,哈哈,欢迎来1691919529 QQ交流
      • _Sven: :kissing_heart: 超级非常感谢!看了你这个我这个新手对界面的实现终于有了比较清晰的了解!
        ManoBoo:@callmeJoeJoe没事没事,有收获就好😄
      • Joe_lisa:认真看了下,不知道是对swift懂的比较少,还是怎么回事,感觉那个友盟问题还是没有解决,那个是在frameswork ,,打开没有看到那个包,在你百度云里面下载的也是一样
        ManoBoo:@Joe_lisa 啊,不是因为Github上传的时候默认不上传.Ds_store文件,所以导致这里有个Bug,可以手动随便复制一个ds_store文件到那个目录下面就没事了😄
      • bc3d3e66fba3:我去,这么牛,我要向你学习学习,请问你的数据怎么抓包的???
        ManoBoo:@臧秋洁 有的
        bc3d3e66fba3:@ManoBoo 好的,有没有微信,加下,以后好交流
        ManoBoo:@臧秋洁 我有另一篇文章,就是说如何模仿一个App的,里面有抓包的教程😊,欢迎参考
      • 6054a4da381e:很感谢,下载下来研究先。
        ManoBoo:@huIsendinfo 😊
      • HaibaraAii:问个问题啊,半糖首页如果用collection view 来写的话,那首页上半部分放入collection view 的section header里面,但是collection view 的cell是横着滚的,一旦设置为scroll horizontally, section header 不也会变成横着滚么
        ManoBoo:@HaibaraAii 兄弟,是这样的,首页collectionView只有一个section,好多cell,所以底下cell的横向滚动不会影响sectionHeaderView,
        HaibaraAii:@ManoBoo 那你提到的首页上半部分不是collection header view 么,莫非这只是个普通的view 首页的最底层铺了一层scroll view 么
        ManoBoo:@HaibaraAii 我这个项目首页没有用sectionHeaderView,不过你说的这种情况我没有试过,我试试去
      • 家丁三锅:厉害,赶紧mark……我决定就学习你的这个开始swift实战演练:blush:
        ManoBoo:@Cosy_Freedom 嗯呢,我的百度云有我的标注什么的😁
        ManoBoo:@Cosy_Freedom 😄
      • d51cc487d74f:强! swift 写起来表示 懵逼 :sob: 把首页上拉渐变效果 修改一下就更棒了
        ManoBoo:@DDlili 加油哦
        d51cc487d74f:@ManoBoo 😀我也尝试写一下
        ManoBoo:@DDlili 是呐,前期首页没有规划好,监听TableView的contentoffset变化,然后应该修改透明度,不过呀,谢谢你的意见😊
      • 6afb6b3c28f9:马克
        ManoBoo:@youyouloveiOS 😏
        6afb6b3c28f9:@ManoBoo 😱
        ManoBoo:@youyouloveiOS 😄
      • 风往北吹_:怎么把我想做的工作给做了:smile:。灰常棒,滋次滋次。:sparkles:已给。
        ManoBoo:@风往北吹_ 嘿嘿,你也准备高仿半糖吗😁,谢谢你的✨
      • sunlin1234:嘿嘿学习一下
      • sunlin1234:我下载后会出现的一个错误
        ManoBoo:@sunlin1234 http://pan.baidu.com/s/1c1qoK5i 非常抱歉,由于GitHub的问题。
        sunlin1234: @ManoBoo 嗯嗯
        ManoBoo:@sunlin1234 稍等,我给你发一个百度云的链接
      • 火耳33:我来点赞 :smiley:
        ManoBoo:@火耳33 谢谢啦😊
      • sunlin1234:太赞了,我就在学习。!!感谢分享
        ManoBoo:其实没有想象的那么难,这几天我会写几篇文章,介绍一下,请稍等:blush:
        sunlin1234: @ManoBoo 嗯嗯。那个抓包数据,是怎么弄的呢。?感觉很厉害
        ManoBoo:@sunlin1234 有什么问题可以交流:grin:
      • MetaZZZZ:学的好快,看你文章里说是全代码,为什么不用storyBoard啊
        ManoBoo:@大尧lllll 😄,一方面公司要让纯代码开发,一方面感觉拖来拖去的,屏幕装不下,,自己也没去熟悉一下,其实StoryBoard做东西还挺快的
      • Joy___:四个月就这么屌 惭愧
        ManoBoo:@Martin_wjl 嘿嘿,没有没有,维尼的小熊是我老师,不会我就问他呢
      • 只爱Ftype:你好, 刚入门做ios app 想请问你一个问题,请问微信这类app,是怎么处理头像图片的呢?因为我在仿做上传头像这个功能的时候只对图片做了压缩质量而没有做裁剪大小的处理,所以头像在好友列表的比较小的frame里显示的时候,图片质量显得过于清晰,所以在上传的时候是不是除了压缩图片质量,还得做裁剪大小的处理去适应好友列表cell里面的imageView的大小?如果是要裁剪的话,用户点击头像缩略图查看头像大图的时候,这个大图是由裁剪过的图直接拉伸过来还是上传的时候也保存了未经裁剪的大图?
        ManoBoo:@火耳33 别:sweat:,这只是我的想法……
        火耳33:@ManoBoo 成大师了 :+1:
        ManoBoo:@只爱Ftype 服务器存的应该是一个图片,我认为,你可以在显示到cell的时候,在本地对图片进行压缩裁剪处理,查看大图的时候显示原来的图片即可
      • Top_熊:自学成才啊 :smiley:
        ManoBoo:@维尼的小熊 师从名门,没办法。。
      • NEWWORLD:现在都已经用Swift写app了呀,本囧感觉已经落伍了 楼主666666 :kissing_heart:

      本文标题:Swift-高仿半糖App

      本文链接:https://www.haomeiwen.com/subject/kddnkttx.html