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