IOS-Swift2.0 高仿半糖App
寫在前面的話
--少年我是去年畢業(yè)做IOS開發(fā)的,這個項目大概是2016年1月份到現(xiàn)在做完的,項目剛開始沒學(xué)過Swift語言,但想著不會就該去挑戰(zhàn),于是邊學(xué)習(xí)邊做這個項目,現(xiàn)在對Swift也有了一定的了解
,在此感謝維尼的小熊的指導(dǎo)
關(guān)于項目(GitHub地址在文章最下方)
--這個開源項目為半糖,官網(wǎng)?,類似于美麗說,一款電商App,使用語言:Swift2.0,開發(fā)工具: Xcode 7.1,純代碼開發(fā),耗時兩個多月,數(shù)據(jù)為本地數(shù)據(jù),用Charles進(jìn)行抓包。
--項目測試機(jī)為5S,UI沒有對6/6S進(jìn)行適配
--因為開發(fā)時間有限,APP里面的各種分類相當(dāng)繁多,因此首頁中測試的話點(diǎn)第一個分類,第一個cell,一般都是有數(shù)據(jù)的,沒有也會提示。
--我是個新手,Bug是難免的嘛。如果發(fā)現(xiàn)有Bug和疑問,請向我聯(lián)系,QQ、簡書、微博都可以
--Tips:首頁支持3D Touch,可以試試哦~
項目效果圖
首頁展示

首頁-清單展示

首頁-搜索分類展示

3D Touch展示(用6S及6S以上測試)

廣場展示

秀

消息(這一部分??,我沒好友,沒粉絲,抓包木有數(shù)據(jù),所以就按照自己的想法做咯)

個人中心(換頭像請用真機(jī)測試~)

項目詳細(xì)講解 (按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() 使用戶進(jìn)入引導(dǎo)頁,動畫完成后,點(diǎn)擊『開啟App之旅』,引導(dǎo)頁發(fā)出 通知,appDelegate接受通知,再將self.window?.rootViewController = self.tabBarController
首頁-展示
①最好的設(shè)計思路應(yīng)該是這樣的:

Tips:以下的思路 在個人中心很完整,前去看看哈
1.紅色部分設(shè)計為 collectionHeaderView
方法: 初始化collecitonView時 在其layout參數(shù)中設(shè)置
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.藍(lán)色部分為顯示的tableview,添加到collectionView的cell.contentView進(jìn)行顯示
Tips:現(xiàn)在很多App的啟動頁面也是這個思路,整體為一個UICollectionView,每一個cell都添加UITableView進(jìn)行顯示
②關(guān)于cell 動態(tài)高度的問題,上圖:

藍(lán)色的這一部分是清單詳情中的描述內(nèi)容,JSON中的描述文字都不一樣,所以導(dǎo)致整個cell的高度都是動態(tài)不定的
Tips:
1.在cell所有的model中添加
cellHeight字段,在tableView代理方法heightForRowAtIndexPath中,判斷model中的cellHeight字段是不是為空,有值則返回該值,類似
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
//model為對應(yīng)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:其實(shí)App中應(yīng)用最多的也就是不用點(diǎn)進(jìn)去看。可以預(yù)覽下一級顯示的內(nèi)容,比如 微信

上代碼:
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即為當(dāng)前控制器self,sourceView即為感應(yīng)3DTouch功能的View,當(dāng)前控制器中,我選擇的是self.view
2.控制器上添加代理UIViewControllerPreviewingDelegate,實(shí)現(xiàn)方func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?和func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController),
看代碼:
①這一部分其實(shí)就兩個步驟
- 1.給3D Touch 你要預(yù)覽的ViewController
- 2.設(shè)定3D Touch預(yù)覽控制器的尺寸大小
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
//-----這一過程這么繁瑣 說白了就是找到當(dāng)前手指點(diǎn)到的 tableviewCell,后面 3Dtouch 預(yù)覽的時候會用到 原尺寸 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!)
//預(yù)顯示的尺寸
detailListContrller.preferredContentSize = CGSizeMake(SCREEN_WIDTH, 600)
//源尺寸
previewingContext.sourceRect = cell.frame
return detailListContrller
}
②對即將預(yù)覽的ViewController,做一些操作,比如隱藏tabbar,隱藏導(dǎo)航欄等等,然后顯示出來
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
showViewController(viewControllerToCommit, sender: self)
}
廣場-展示
設(shè)計思路如圖:

與首頁相似,紅色部分View是藍(lán)色 mainCollectionView的headerView,紅色View中又包含著一個collectionView
下拉刷新控件:

Tips:下拉刷新控件 使用SVPullToRefresh 框架,然后在它的文件中修改了一個方法,把自己想要出現(xiàn)的動圖加了進(jìn)去
- (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"];
//設(shè)置刷新動圖只在Loading狀態(tài)下顯示
[self setCustomView:gifImgView forState:SVPullToRefreshStateLoading];
}
return _arrow;
}
秀出自我
這一部分的實(shí)現(xiàn)思路比較簡單
1.在appdelegate中,實(shí)現(xiàn)tabbarControllerDelegate方法,shouldSelectViewController中,判斷每次點(diǎn)擊的是不是ShowMeController,如果是,則返回false,然后在當(dāng)前的控制器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 {
//點(diǎn)擊 '消息中心' 延遲5s 后發(fā)出通知
//模擬網(wǎng)絡(luò)刷新
viewController.tabBarItem.title = nil
postNotificationCenter(tabbarController.viewControllers!)
}
return true
}
2.對于調(diào)用照相機(jī)的方法,首先判斷有沒有攝像頭
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) {
imagePicker.sourceType = UIImagePickerControllerSourceType.Camera
presentViewController(imagePicker, animated: true, completion: nil)
}else {
TipView.showMessage("騷年,找個有攝像頭的手機(jī)吧。。")
}
然后viewController中添加代理 UIImagePickerControllerDelegate,UINavigationControllerDelegate
在代理方法中可以獲取到剛拍好的圖片進(jìn)行處理
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
}
}
消息這一部分就很少東西了,因為我沒有粉絲,沒有消息??,
為了模擬后臺發(fā)送消息,在tabBarController的"shouldSelectViewController"中,當(dāng)點(diǎn)擊該控制器時,appdelegate發(fā)出通知,MessageViewController中接收通知,提示用戶收到消息
//接受 模擬后臺的推送、、、、 NSNotificationCenter.defaultCenter().addObserver(self, selector: "hasNewMessage", name: UserHasNewMessage, object: nil)
收到通知以后執(zhí)行的方法:
func hasNewMessage(){
print("收到通知了。")
TipView.showMessage("您有新的消息。但是你看不見??")
}
個人中心
這一部分設(shè)計思路與廣場類似,也都是上面是HeaderView,下面是UICollectionView,不同的是個人中心最上面有一個背景圖片,會隨著collectionView的contentOffSet變化而放大
設(shè)計思路:

1.黃色View為 UIImageView,處于整個View的下層,collectionView和它的headerView的backgroundColor = UIColor.clearColor()
最后在func scrollViewDidScroll(scrollView: UIScrollView)中調(diào)整 backImageView的transform屬性即可
2.關(guān)于??繂栴},

上圖

,姑且叫它
titleView, titleView滾到設(shè)定位置時會??康綄?dǎo)航欄底下,思路如下:
- titleView是添加到headerView中的,在
scrollViewDidScroll方法中,監(jiān)聽collectionView的contentOffset.y的變化,當(dāng)titleView到達(dá)設(shè)定的位置時,titleView.removeFromSuperview(),然后計算frame,將其添加到View上,這樣就做除了停靠的效果,這樣做比較簡單
??: 剛開始我單獨(dú)將titleView添加到view上,collectionView滑動時還得一直更改titleView.frame
聊聊感想哈
其實(shí)從OC轉(zhuǎn)向Swift非常簡單,這個項目不難,很適合從OC轉(zhuǎn)向swift,或swift初學(xué)者。
少年也是第一次發(fā)布自己的項目,忘老鳥輕拍,新手共勉,有什么問題或bug聯(lián)系我哈,歡迎來我的GitHub上賞個Star??
GitHub 代碼下載地址
代碼下載地址,給個star再走??
直接打開運(yùn)行工程

收到有童鞋反應(yīng)說,會遇到這樣的Bug導(dǎo)致無法運(yùn)行

Tips:由于GitHub上傳的問題,解決辦法如下:
