獻給初學iOS的小盆友們------微博app專案開發之一專案初始化
獻給初學iOS的小盆友們——微博app專案開發之一 專案初始化
本人自學iOS也有七八個月了,不敢說學到很深入了,但也算入了門。此次微博app專案參考了傳智播客培訓教材,主要學習內容有架構思想,封裝思想,程式碼重構,業務邏輯等內容,專案涵蓋面廣泛,講解易懂,且採用純程式碼方式搭建UI,希望對那些沒有時間看視訊的初學者們有所幫助。相信學習完本套專案,初學者會在程式設計思想上有一個很大的提升。
內容
- 專案素材獲取
- 環境配置
- 自定義tabBarController
- 修改tabBar內部結構
- 劃分結構
本節資料
1.1 專案素材獲取
首先模仿一個專案,需要圖片等素材,單憑自己是做不出來的。本專案提供了基本的圖片素材供下載使用,一般專案素材的獲取步驟如下:
- 開啟蘋果電腦iTunes應用,選擇頂部欄“AppStore”項
- 然後在搜尋框內輸入“微博”並搜尋
- 找到微博並點選,進去後點選微博圖示下的“取得”按鈕
- 輸入 iCloud賬戶密碼後即可下載
- 左上角下載按鈕可以顯示下載進度
- 點選頂部欄“我的iPhone應用按鈕”
- 找到微博應用,右鍵點選,選擇“在Finder中顯示”
- 可以看到下載的是ipa型別的檔案,使用解壓檔案解壓後即可得到微博應用資料夾
- 進入資料夾後,選擇Payload下的Weibo.app檔案,右鍵點選後選擇“顯示包內容”,即可看到微博應用所需的所有圖片,以後專案模仿都會用得到
1.2 環境配置
開發任何一個大型的應用都需要提前對開發環境進行配置,本次微博專案對Xcode進行了能滿足我們模仿要求的簡單設定。配置過程也就是修改info.plist檔案而已,點選“微博模擬”專案出現的設定頁面就是info.plist的圖形化介面。配置過程如下:
- Bundle Identifier 設定
Bundle Identifier 主要作用有app在上傳app store時為了區分不同程式 時使用,開發推送功能時需要,在這裡設定成YGWeibo.- - - - 。 - Version 版本號
以後迭代開發時,版本號必須比之前的大,在這裡不需要設定 - Development Target
選擇7.0以後的都可以 - Devices
選擇iPhone - Main Interface
此次專案採用純程式碼建立,所以不需要載入storyboard,在這裡設定為空,並且把左側Main.Storyboard,ViewController.h,ViewController.m 檔案刪除。 - Device orientation
只選擇portrait - Status Bar Style
選擇default後,勾選Hide Status Bar
這裡講一講,怎麼用純程式碼得到跟載入main.storyboard有相同效果的介面。首先蘋果應用程式的啟動步驟是這樣的,在一開啟時,首先進入main函式,main 函式內主要執行三個步驟,首先建立UIApplication物件,然後建立AppDelegate物件,並且成為UIApplication物件的代理屬性,然後開啟主執行緒迴圈,最後載入info.plist檔案,判斷是否有main.storyboard,如果有,就會載入main.storyboard。因為我們這裡才用純程式碼開發,所以info.plist就沒有main.storyboard檔案了,需要在AppDelegate.m裡的第一個代理方法中設定視窗,以及建立並載入檢視控制器,程式碼如下,但是此程式碼非最終的tabBarVC的設定程式碼,以後會有修改此處只做演示用。此程式碼就相當於載入Storyboard的步驟。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 建立視窗
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 為了展示效果 設定背景色為黃色
self.window.backgroundColor = [UIColor yellowColor];
// 建立tabBarViewController
UITabBarController *tabBarVc = [[UITabBarController alloc] init];
tabBarVc.view.backgroundColor = [UIColor redColor];
// 管理子控制器
// 首頁
UIViewController *home = [[UIViewController alloc] init];
home.view.backgroundColor = [UIColor greenColor];
[tabBarVc addChildViewController:home];
// 訊息
UIViewController *message = [[UIViewController alloc] init];
message.view.backgroundColor = [UIColor blueColor];
[tabBarVc addChildViewController:message];
// 發現
UIViewController *discover = [[UIViewController alloc] init];
discover.view.backgroundColor = [UIColor purpleColor];
[tabBarVc addChildViewController:discover];
// 我
UIViewController *profile = [[UIViewController alloc] init];
profile.view.backgroundColor = [UIColor lightGrayColor];
[tabBarVc addChildViewController:profile];
// 設定視窗的根控制器
self.window.rootViewController = tabBarVc;
// 顯示視窗
[self.window makeKeyAndVisible];
return YES;
}
- 設定AppIcon
直接拖拽素材資料夾內的AppIcon資料夾到Xcode裡Assets.xcassets內的AppIcon裡即可 - 設定啟動圖片
在專案General 設定裡面找到Launch Images Source 欄,點選後出現對話方塊,選擇migrate,然後點選其後的灰色按鈕,會發現自動跳轉到Brand Assets 欄內。開啟素材資料夾中的LaunchImages資料夾,拖拽檔案到Brand Assets 內,出現綠色加號後放開即可,最後刪除Launch Screen File 欄內的東西。
但是這種方法不如直接載入Launch Screen.storyboard資料夾更強大,因為Launch Screen.storyboard可以展示更多的東西,且不需要美工出很多啟動圖片,因為其可以自動佈局以適應各種大小螢幕。如果不刪除Launch Screen.storyboard,則會優先載入Launch Screen.storyboard內容。
1.3 自定義tabBarController
1.3.1 更改AppDelegate.m
因為AppDelegate.m檔案以後會越來越大,應該自定義一個類專門用於建立和管理tabBarController。在新建檔案之前,要設定個字首,以方便區別和管理。選擇左邊欄微博模擬專案,找到最右邊欄有個“Class Prefix”欄,寫上你想起的字首名稱即可,如下圖所示。
更改AppDelegate.m內容為如下程式碼:(首先要新建YGTabBarController)
#import "AppDelegate.h"
#import "YGTabBarController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 建立視窗
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//建立自定義tabbarcontroller
YGTabBarController *tabBarVc = [[YGTabBarController alloc]init];
// 設定視窗的根控制器
self.window.rootViewController = tabBarVc;
// 顯示視窗
[self.window makeKeyAndVisible];
return YES;
}
在此,提醒下UITabBarController內的view在一建立控制器的時候[[YGTabBarController alloc]init]
就會載入View,並執行ViewDidLoad方法,所以其View不是懶載入的。但是一般的UIViewController的View是懶載入的。
1.3.2 新建YGTabBarController。
新建一個YGTabBarController類繼承自UITabBarController後,然後把之前在AppDelegate內建立與載入子控制器的程式碼封裝在YGTabBarController.m中,並把tabBarVc更改為self。
本小節主要任務就是為了程式碼的簡潔性,抽取兩個方法,一個是從ViewDidLoad中抽取設定tabBarController子控制元件的方法:
-(void)setUpAllChildViewController
因為每加一個子控制器所寫的程式碼都是類似的,所以又在上個方法內又抽取了設定一個子控制元件的方法。因為每個子控制器都要設定標題,顏色,圖片以及其他屬性,所以把這些屬性都設定為引數傳進到方法內。這裡提醒一下,如果引數中會傳入中文的話,一般把此引數放到最後,例如title引數。
- (void)setUpOneChildViewController:(UIViewController *)vc image:(UIImage *)image selectedImage:(UIImage *)selectedImage title:(NSString *)title
下面就要講如何設定tabBar按鈕上面的文字與圖片了。
首先tabBar上的按鈕是由對應的子控制器的tabBarItem屬性決定。匯入素材中tabBar資料夾拖入到專案中。設定按鈕title和image。在ios7之後,預設會把UITabBar上面的按鈕圖片渲染成藍色,但大多情況下藍色不是我們想要的顏色,我們想要讓tabBar用美工設計好的圖片顏色。更改的方法可以是,如圖片所示, 把每張帶有selected字元字尾的圖片的Render As 屬性設定為Original Image。
或者用程式碼更改,在這裡我們就寫一個UIImage的分類——UIImage+Image.h/.m。目的是為了讓以後也方便設定圖片渲染。程式碼如下:
#import "UIImage+image.h"
@implementation UIImage (image)
+ (instancetype)imageWithOriginalName:(NSString *)imageName
{
UIImage *image = [UIImage imageNamed:imageName];
return [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}
@end
這裡對上面的程式碼做下說明:為什麼要使用工廠方法(返回值為instancetype)?因為其預設識別是哪個類呼叫此方法,並返回該類的物件。
然後在setUpOneChildViewController方法裡設定按鈕標題等內容。程式碼如下:
#pragma mark - 新增一個子控制器
- (void)setUpOneChildViewController:(UIViewController *)vc image:(UIImage *)image selectedImage:(UIImage *)selectedImage title:(NSString *)title
{
vc.tabBarItem.title = title;
vc.tabBarItem.image = image;
vc.tabBarItem.badgeValue = @"10";
vc.tabBarItem.selectedImage = selectedImage;
[self addChildViewController:vc];
}
但是在設定字型顏色的時候你會發現,不能用self.tabBarItem.textColor 來設定標題顏色,因為UITabBarItem是模型而不是控制元件,模型是用來儲存資料以決定tabBar上按鈕的內容。只有控制元件才有textColor屬性,例如UILabel。所以通過觀察UITabBarItem的標頭檔案以及其父類的標頭檔案,發現可以用setTitleTextAttributes 方法 也就是富文字來設定標題顏色。富文字不僅可以設定控制元件的文字顏色,也可以設定字型空心,陰影,圖文混排等。通過UIKit框架內的NSAttributesString.h標頭檔案,可以查詢到設定富文字字典等key。這裡我們採用全域性方法+ initialize 來設定顏色。但在+load方法內也可以。但是兩者的呼叫事件不同,+initialize方法是在第一次使用這個類或者子類的時候呼叫。+load方法是在程式一啟動的時候就呼叫,然後把所有的類載入到記憶體,且先於main函式執行。
initialize 程式碼如下:
+ (void)initialize
{
// 這行程式碼是獲取所有的tabBarItem外觀標識,這樣就容易把不希望改動到外觀也更改了
// UITabBarItem *item = [UITabBarItem appearance];
//一般採用帶 self的方法,這裡self就是指 YGTabBarController
// 獲取當前這個類下面的所有tabBarItem
UITabBarItem *item = [UITabBarItem appearanceWhenContainedIn:self, nil];
NSMutableDictionary *att = [NSMutableDictionary dictionary];
//這裡也可以用[att setObject:[UIColor orangeColor] forKey:NSForegroundColorAttributeName];
//來設定富文字字典,兩種方法都可以。
att[NSForegroundColorAttributeName] = [UIColor orangeColor];
[item setTitleTextAttributes:att forState:UIControlStateSelected];
}
1.4 修改tabBar內部結構
在觀察實際微博介面會發現,tabBar中間有個用來發微博的加號按鈕。這就需要首先調整系統tabBar 結構,改變子控制元件位置,然後在中間加上加號按鈕 。系統的tabBar按鈕位置是不能改動的,所以智慧自定義一個YGTabBar,繼承自UITabBar。然後把系統TabBar替換成自定義的YGTabBar。程式碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
//新增子控制器
[self setUpAllChildViewController];
// 自定義tabBar
YGTabBar *tabBar = [[YGTabBar alloc] initWithFrame:self.tabBar.frame];
// 利用KVC更改readonly的屬性
[self setValue:tabBar forKeyPath:@"tabBar"];
}
程式碼說明: 這裡有兩點需要注意,一要判斷在哪個方法裡去替換系統tabBar。經過測試發現,系統tabBar上的button是在執行完ViewDidLoad 和ViewWillAppear方法後才新增上去的。如下圖所示:
從上如可以看到沒給方法執行的順序,所以我們要在新增tabBarButton前替換系統tabBar,這樣的話,tabBarButton就會新增到自定義的YZTabBar上面。
第二個問題就是,但是這樣賦值self.tabBar == tabBar 會出現錯誤。因為tabBar是readOnly屬性,不能這樣賦值。但是我們可以使用KVC賦值,如上面程式碼所示。
替換完系統tabBar之後,就要重寫YGTabBar.m 內的 layOutSubviews方法,來計算每個item的位置。在重寫之前先新增一個發微博按鈕。程式碼如下:
- (UIButton *)plusButton
{
if (_plusButton == nil) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:@"tabbar_compose_icon_add"] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:@"tabbar_compose_background_icon_add"] forState:UIControlStateHighlighted];
[btn setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button"] forState:UIControlStateNormal];
[btn setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button_highlighted"] forState:UIControlStateHighlighted];
// 預設按鈕的尺寸跟背景圖片一樣大
// sizeToFit:預設會根據按鈕的背景圖片或者image和文字計算出按鈕的最合適的尺寸
[btn sizeToFit];
_plusButton = btn;
[self addSubview:_plusButton];
}
return _plusButton;
}
重寫layoutsubviews方法,調整子控制元件位置,程式碼如下:
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat w = self.bounds.size.width;
CGFloat h = self.bounds.size.height;
CGFloat btnX = 0;
CGFloat btnY = 0;
CGFloat btnW = w / (self.items.count + 1);
CGFloat btnH = self.bounds.size.height;
int i = 0;
// 調整系統自帶的tabBar上的按鈕位置
for (UIView *tabBarButton in self.subviews) {
// 判斷下是否是UITabBarButton
if ([tabBarButton isKindOfClass:NSClassFromString(@"UITabBarButton" )]) {
if (i == 2) {
i = 3;
}
btnX = i * btnW;
tabBarButton.frame = CGRectMake(btnX, btnY, btnW, btnH);
i++;
}
}
// 設定新增按鈕的位置
self.plusButton.center = CGPointMake(w * 0.5, h * 0.5);
}
程式碼說明:在取出tabBar每個button子控制元件時,需要判斷類,可以利用NSClassFromString來根據一個字串得出其類名。在interface 內新增一個UIButton 屬性,並且懶載入,利用sizeToFit來預設按鈕尺寸和背景圖片一樣大。但是我們發現tabBar上的UIBadgeView (如圖所示)是繼承自UIView的,是系統自帶的,沒法通過設定圖片的方法更改。下一個微博將會講到如何設定自定義的badgeView。
1.5 劃分結構
YGTabbar上現在有五個模組,每個模組都有自己的功能,如果像我們這樣直接建立每個模組的ViewController,就不能重寫每個控制器的viewDidLoad方法,也就沒法新增每個模組的各種功能了。這就需要我們為每個模組建立各自的MVC檔案,劃分結構如圖所示: