一款跑步App iOS完整實現 (附源碼和說明)

一款記錄跑步應用,能記錄運動軌跡和運動過程中的數據,功能實現比較完整,15年年底做的上架項目,開發者賬號過期AppStore上已經搜不到了,不過一些Android應用市場還能找到Android的版本。

源碼放出來作為學習交流。見github

應用截圖

應用大概長這個樣子,截取了部分界面

1.jpg

2.jpg

3.jpg

4.jpg

項目文件結構

項目的文件結構如下所示

5.jpeg

界面相關的實現全在View里,其他文件是一些比較獨立的且和整個項目相關的功能的實現。

個人偏好是以功能模塊來劃分文件,如跑步相關的功能會專門放一個文件里,用戶相關的功能又會放另一個文件里??雌渌恍╅_源項目,比如以MVVM為架構的工程,會專門分為ModelViewController、ViewModel三個文件夾,然后所有相關的文件無論功能劃分全堆到對應的文件里。這里會存在一個問題,當應用比較復雜的時候,每個文件夾下便會存在很多文件,從中去找要修改的文件很麻煩,而且修改同一個功能還要跨文件夾工作。

還有一種方式是文件夾按功能來劃分,然后在每個實現對應功能的文件下再按MVVM或者MVC架構的方式劃分對應的文件夾,看個人喜好。

界面的實現,個人習慣是將界面中關聯較緊密的部分,單獨抽出來作為一個獨立的控件,對外提供修改數據的接口,視圖控制器直接引用這個控件,并通過外部接口來修改該控件的顯示,而不用關注控件內部例如布局相關、UI設置的具體實現。有時候控件里面也會包含其他的控件。總之就是為了將對外無關的代碼實現封裝到類中,外部操作控件時只需要關注邏輯實現。

以前公司的項目,見過一個視圖控制器類,包含所有視圖的生成布局設置,和相關業務邏輯的處理,結果就是一個UIViewController類文件的代碼行數近3萬行,簡直是維護人員的噩夢。所以個人偏向將比較獨立的代碼單獨抽出為一個文件,無論是界面布局相關,或是業務流程處理相關,盡量保證每個文件的代碼在幾百行之內。

布局方式

大部分視圖控件使用了xib做自動布局,沒用storyboard做頁面間的跳轉,是因為大部分時間用筆記本做開發,小屏幕看storyboard一堆連在一起的界面體驗太差。當時開發的時候還是15年年底,iOS9的reference也才剛出,項目需要兼容iOS8。

有些視圖的布局,需要的運算很小,懶得專門新建個xib,就直接通過計算frame的方式來實現了。

有些使用了xib但需要做布局變化的地方,會引用布局約束NSLayoutConstraint,通過代碼方式對布局約束進行調整。

也有些地方嘗試使用純代碼定義NSLayoutConstraint的方式來實現自動布局,感受就是,實現的代碼量太大,不方便后續維護修改。

抽點代碼出來感受一下。。這段代碼其實在xib里面就是給一個視圖添加了一個上下左右邊緣的約束和對應的間距,界面上點幾下就能實現的事,換成代碼會要用好多行。所以項目里的布局能用xib的基本全用xib了,我懶。

...

[self addConstraint:[NSLayoutConstraint constraintWithItem:_contentView
                                                     attribute:NSLayoutAttributeTop
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:self
                                                     attribute:NSLayoutAttributeTop
                                                    multiplier:1.0
                                                      constant:0.0]];
    
    [self addConstraint:[NSLayoutConstraint constraintWithItem:_contentView
                                                     attribute:NSLayoutAttributeBottom
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:self
                                                     attribute:NSLayoutAttributeBottom
                                                    multiplier:1.0
                                                      constant:0.0]];
    
    [self addConstraint:[NSLayoutConstraint constraintWithItem:_contentView
                                                     attribute:NSLayoutAttributeLeading
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:self
                                                     attribute:NSLayoutAttributeLeading
                                                    multiplier:1.0
                                                      constant:0.0]];
    
    [self addConstraint:[NSLayoutConstraint constraintWithItem:_contentView
                                                     attribute:NSLayoutAttributeTrailing
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:self
                                                     attribute:NSLayoutAttributeTrailing
                                                    multiplier:1.0
                                                      constant:0.0]];
                                                      
    ...

若真想用代碼來實現自動布局,推薦一個框架Masonry,它對NSLayoutConstraint做了一層封裝,用一種更直觀更優雅的編碼方式來實現布局約束。這個框架有對應的Swift版本SnapKit,也是同一個團隊在進行維護。具體可查看官方文檔。

第三方庫

主要用到的第三方庫:

  • 高德地圖SDK:用來做路徑繪制和定位
  • ShareSDK:第三方分享和登錄
  • FMDB:本地數據庫的操作
  • AFNetworking:網絡請求
  • 友盟SDK:做埋點統計和用戶反饋

使用到的第三方庫都放到了項目Vendors文件夾里,當時是直接將第三方的源碼和資源拖到項目里,并手動給項目target添加需要的系統庫。這并不是一種好的實踐方式,引用管理第三方還是推薦使用CocoaPods。當然,這種做法也有一種好處,就是方便別人把項目download下來之后什么都不用管直接就能跑起來了。:)

藍牙連接

應用可以通過藍牙連接外設獲取數據。我們有配套的運動內衣,穿戴上之后可收集心率。應用通過掃描附近藍牙設備并連接就可獲取到心率的數據。

整個藍牙相關的流程是標準的藍牙協議,iOS自帶相關框架<CoreBluetooth/CoreBluetooth.h>專門處理這個流程。

代碼具體實現在 Bluetooth 文件夾下的 YSBluetoothConnect 類。

實現對應的代理來進行藍牙事件回調處理,如設備的連接、斷開,心率數據的獲取。

CBCentralManagerDelegate
CBPeripheralDelegate

心率數據貌似在藍牙設備中有固定的標準字段,代碼里面這個字段的常量。

static NSString *ServiceHeartRateUUIDStr = @"180D";
static NSString *CharacteristicHeartRateUUIDStr = @"2A37";

所以應該只要是能發送心率的藍牙設備都能給這個應用傳輸數據。代碼里面對藍牙設備名稱的前綴做了判斷處理,若想嘗試自行連其他藍牙設備可以把這個前綴判斷注釋掉。

有些界面需要有藍牙數據才能顯示,為了獲取到模擬的藍牙數據,直接將這個字段設為YES即可。

// 設為YES時模擬生成心率
static BOOL simulationMode = YES;

語音提示

Voice 文件夾下的 YSVoicePrompt類,運動時每公里提示,若連接心率設備時,會根據心率是否在特定的范圍做相應提示。提示可設置男聲女聲,資源文件在Audio.bundle里,音頻資源當時還是專門上淘寶找人做的。

數據庫操作

主要實現在 Database 文件下。FMDB對系統自帶的SQLite做了封裝,使得可以直接用OC的方式來操作數據庫。

數據庫主要保存用戶信息和每次跑步數據,例如每次跑步記錄的GPS路徑坐標,運動過程中收集到的心率數據。界面展示相關信息時需要從數據庫查詢。

系統的sqlite并非線程安全,多線程同時對sqlite進行操作極有可能造成崩潰。數據庫的讀寫比較耗時不應該放到主線程里,多線程操作可以放到FMDBFMDatabaseQueue隊列里進行。

網絡請求

請求相關的處理實現在 Network 文件下。如用戶的注冊時的驗證碼,登錄注銷,云端數據同步和本地數據上傳等。具體接口可看 YSNetworkManager.h 文件注釋。

租用的服務器已過期,所以現在的請求基本都返回超時,只能看著接口自行腦補過程了。

地圖功能

整個應用最核心的功能就是和地圖相關的路徑記錄了。使用的是高德SDK。具體的功能實現在 Manager/Map 文件下。

主要的實現類為 YSMapManager

運動開始結束時調用的接口

- (void)startLocation;
- (void)endLocation;

MAMapView 為顯示地圖的視圖,用來顯示地圖和繪制運動路徑。

實現MAMapView的代理MAMapViewDelegate,定位實時更新時會收到對應的回調

- (void)mapView:(MAMapView *)mapView didUpdateUserLocation:(MAUserLocation *)userLocation updatingLocation:(BOOL)updatingLocation

回調參數里會包含新的定位的信息,將新獲取到的定位數據保存,并重新繪,即可看到實時的運動軌跡。

實時路徑的繪制具體可看代碼,也可看這篇文章,高德地圖實時路徑繪制代碼實現,忽略其他的代碼。

YSMapPaintFunc 類實現了將獲取到的定位點依次連成一條路徑顯示在地圖上。GPS定位有時候會存在一定的誤差,繪制到地圖上會顯示毛刺,實現的時候用了個小算法將這些毛刺點做了平滑處理,具體自己看代碼啦。

記錄路徑的過程中還需要計算一些其他的值,如總距離,平均速度,當前速度,配速等。這些東可以通過搜集到的定位數據和時間來進行計算。YSMapPaintFunc類用來做相應的計算。

計時器

Manager/Time 中實現。YSTimeManager 主要用了一個Runloop通過一定的時間間隔來不斷刷新界面和時間相關的信息。需要注意的是NSRunLoop在主線程和子線程使用的區別。

Manager/CaptchaTimer 中的實現為倒計時,用戶手機注冊時會發送驗證碼,發送驗證碼按鈕點擊之后會有一定的時間將按鈕置灰,以防止頻繁的發送驗證碼。

應用配置

Manager/ConfigYSConfigManager 類用來記錄應用的配置,通過系統的NSUserDefaults進行保存。需要配置的信息較少,也就語音提示選擇男聲或女聲,界面上時候顯示實時心率數據的面板,藍牙默認連接。配置的選項不多。

Models

Models文件加下有很多model,當時初衷是將一些相關的數據保存在一個類里,方便作為參數進行數據傳遞。如單次跑步相關的數據保存到一個model里,傳遞給專門展示這些數據的視圖做為界面的顯示?,F在回過頭看感覺處理得有點亂,也許不是個好的實踐,讀者請自行斟酌參考。

應用的界面實現

界面相關的代碼實現全在View文件夾下。

6.jpeg

文件夾對應的功能實現

login

用戶相關功能的實現,如注冊登錄,修改重置密碼等。

Settings

設置界面,提示音設置,打開關閉心率面板,用戶反饋。

Calendar

tabBar左邊的日歷界面,當時想用UICollectionView來實現個日歷控件,后面調試的時候發現有些日期cell之間的左右間隔在設為0之后還是會出現細線,不得以只能強行用一個UIScrollView在上面自己貼UIView的方式來實現了這控件。性能還有待優化,左右滑動切換日期時有時會有卡頓現象。

Run

主要的幾個界面:

  • YSRunViewController:tabBar中間的界面,顯示記錄的總體數據,開始運動的入口
  • YSRunningRecordViewController:運動過程中界面,主要包含兩個視圖,顯示實時運動軌跡的地圖視圖,和不顯示地圖的視圖
  • YSResultRecordView:運動結束后顯示結果的界面

User

tabBar右邊的用戶界面,顯示當前用戶信息,登錄注銷,修改用戶資料的入口。

SportRecord

記錄單次運動具體信息和數據分析的界面。

幾個文件實現的功能:

  • Detail:詳情界面,此次運動的距、時間、速度等
  • HeartRate:心率數據界面,分析運動全程心率在各個范圍內的比例
  • Locus:運動的軌跡
  • Pace:計算出每段路程的配速
  • Share:社交分享,將軌跡生成圖片發送

General

一些可以單獨抽出來,或者幾個界面都會用到的控件。大部分也都是xib實現。

xib實現可復用控件時需要考慮一個問題,使用時是直接用在另一個xib中,還是在代碼中通過nibWithNibName的方式加載,兩種使用方式設定Custom Class的地方不一樣,具體參考源碼。

其他

項目的大體情況如上所述,具體的一些細節實現請自行查看源碼。:)

最后

源碼可以隨便使用,不用經過允許。有問題可提issues。

偶然發現騰訊部落小伙伴還在運營,關注數量還挺多,喜歡運動跑步的小伙伴可關注易瘦跑步騰訊部落,每天都會分享運動跑步相關的小知識。:)

完。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容