在上一篇博客中,給大家介紹了一下我們傳統的 APP 界面框架—標籤導航的一些優缺點,在這篇文章中我會來給大家演示,如何用代碼去實現這一框架。本次的實現我會分成倆部分來講,好了閒話少說,接下來進入到開發階段。
先來一張最終的效果圖瞅一眼:
接下來,創建一個 Xcode 工程,我取名叫做CoolFrame,該項目我到時候會託管到 GitHub 上去維護,地址我會在下一篇博文中給出。
根據上圖的樣式,可以把界面分成三部分:導航欄,中間內容,以及底部的TabBar。我們先從簡單的中間內容開始編碼做起,這裏我根據我底部有四個tabbar,所以定義了四個UIViewController,每個UIViewController很簡單,主要是爲了能夠區分我下方點擊的是哪一個TabBarItem,每個UIViewController中都用一個固定的英文字母顯示在正中央,這裏就列舉其中一個界面的代碼:
#import "FirstViewController.h"
@interface FirstViewController ()
@end
@implementation FirstViewController
@synthesize label = _label;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"A";
[self.view setBackgroundColor:[UIColor whiteColor]];
[[self label] setFrame:CGRectMake(roundf(self.view.frame.size.width - 100)/2, roundf(self.view.frame.size.height - 100)/2, 100, 100)];
[self.label setTextAlignment:NSTextAlignmentCenter];
[self.label setFont:[UIFont fontWithName:@"HiraKakuProN-W3" size:40.0f]];
[self.label setText:@"A"];
[self.view addSubview:[self label]];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (UILabel *)label{
if(!_label){
_label = [[UILabel alloc] init];
}
return _label;
}
@end
中間部分很簡單,接下來咱們來說說底部的 tabbar 是怎麼實現的,先來看下效果圖:
我來把效果圖構造拆分成四個 UIButton 和一個 UIView ,這樣是不是就很容易明白了,UIView 作爲一個背景框裏面填補了四個按鈕,當我們選中其中一個按鈕的時候中間就切換到對應的界面,隨後按鈕的背景色也隨之改變。當然,在這裏我們要實現的可以一個可以用作商業用途的框架,所以說我們這裏的按鈕就不可能是 Xcode 給我們提供的按鈕,我們得需要自定義一個按鈕,這樣才能讓我們的 UI 更加的美觀,好了,這裏附上按鈕的代碼:
//
// CustomTabBarItem.m
// CoolFrame
//
// Created by silicon on 2017/7/25.
// Copyright © 2017年 com.snailgames.coolframe. All rights reserved.
//
#import "CustomTabBarItem.h"
#define RGB(r, g, b) [UIColor colorWithRed:(r)/255.f green:(g)/255.f blue:(b)/255.f alpha:1.f]
@implementation CustomTabBarItem
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if(self){
[self commonInitialization];
}
return self;
}
- (id)init{
return [self initWithFrame:CGRectZero];
}
- (void)commonInitialization{
[self setBackgroundColor:[UIColor clearColor]];
_title = @"";
_titlePositionAdjustment = UIOffsetZero;
_unselectedTitleAttributes = @{
NSFontAttributeName: [UIFont systemFontOfSize:10],
NSForegroundColorAttributeName: RGB(0, 0, 0)
};
_selectedTitleAttributes = [_unselectedTitleAttributes copy];
_badgeBackgroundColor = [UIColor redColor];
_badgeTextColor = [UIColor whiteColor];
_badgeTextFont = [UIFont systemFontOfSize:12];
_badgePositionAdjustment = UIOffsetZero;
}
- (void)drawRect:(CGRect)rect{
CGSize frameSize = self.frame.size;
CGSize titleSize = CGSizeZero;
CGSize imageSize = CGSizeZero;
NSDictionary *titleAttribute = nil;
UIImage *backgroundimage = nil;
UIImage *image = nil;
CGFloat imageStartingY = 0.0f;
if([self isSelected]){
image = [self selectedImage];
backgroundimage = [self selectedBackgroundImage];
titleAttribute = [self selectedTitleAttributes];
if(!titleAttribute){
titleAttribute = [self unselectedTitleAttributes];
}
}else{
image = [self unselectedImage];
backgroundimage = [self unselectedBackgroundImage];
titleAttribute = [self unselectedTitleAttributes];
}
imageSize = [image size];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
[backgroundimage drawInRect:self.bounds];
if(!_title){
[image drawInRect:CGRectMake(roundf(frameSize.width / 2 - imageSize.width / 2) +
_imagePositionAdjustment.horizontal,
roundf(frameSize.height / 2 - imageSize.height / 2) +
_imagePositionAdjustment.vertical,
imageSize.width, imageSize.height)];
}else{
titleSize = [_title boundingRectWithSize:CGSizeMake(frameSize.width, 20)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: titleAttribute[NSFontAttributeName]}
context:nil].size;
imageStartingY = roundf((frameSize.height - imageSize.height - titleSize.height) / 2);
[image drawInRect:CGRectMake(roundf(frameSize.width / 2 - imageSize.width / 2) +
_imagePositionAdjustment.horizontal,
imageStartingY + _imagePositionAdjustment.vertical,
imageSize.width, imageSize.height)];
CGContextSetFillColorWithColor(context, [titleAttribute[NSForegroundColorAttributeName] CGColor]);
[_title drawInRect:CGRectMake(roundf(frameSize.width / 2 - titleSize.width / 2) +
_titlePositionAdjustment.horizontal,
imageStartingY + imageSize.height + _titlePositionAdjustment.vertical,
titleSize.width, titleSize.height)
withAttributes:titleAttribute];
}
CGContextRestoreGState(context);
}
#pragma mark - Image configuration
- (UIImage *)finishedSelectedImage{
return [self selectedImage];
}
- (UIImage *)finishedUnselectedImage{
return [self unselectedImage];
}
- (void)setFinishedSelectedImage:(UIImage *)selectedImage withFinishedUnselectedImage:(UIImage *)unselectedImage{
if(selectedImage && selectedImage != [self selectedImage]){
[self setSelectedImage:selectedImage];
}
if(unselectedImage && unselectedImage != [self unselectedImage]){
[self setUnselectedImage:unselectedImage];
}
}
- (void)setBadgeValue:(NSString *)badgeValue{
_badgeValue = badgeValue;
[self setNeedsDisplay];
}
#pragma mark - Background configuration
- (UIImage *)backgroundSelectedImage{
return [self backgroundSelectedImage];
}
- (UIImage *)backgroundUnselectedImage{
return [self backgroundUnselectedImage];
}
- (void)setBackgroundSelectedImage:(UIImage *)selectedImage withUnselectedImage:(UIImage *)unselectedImage{
if(selectedImage && selectedImage != [self selectedBackgroundImage]){
[self setSelectedBackgroundImage:selectedImage];
}
if(unselectedImage && unselectedImage != [self unselectedBackgroundImage]){
[self setUnselectedBackgroundImage:unselectedImage];
}
}
@end
搞定了按鈕,接下來就要把按鈕聚集在一起,所以需要寫一個按鈕容器視圖,不管有多少個按鈕都可以讓他們在這個容器中和諧的鋪展開來,並且在這個類中,還要爲按鈕添加必要的響應事件,容器視圖也是作爲一個子視圖存在,所以我們這邊就定義一個 UIView 即可,具體代碼如下:
#import <UIKit/UIKit.h>
#include "CustomTabBarItem.h"
@protocol CustomTabbarDelegate;
@interface CustomTarbar : UIView
@property (nonatomic, strong) id<CustomTabbarDelegate> delegate;
@property (nonatomic, strong) NSArray *items;
@property (nonatomic, strong) CustomTabBarItem *selectedItem;
@property (nonatomic, strong) UIView *backgroundView;
@property (nonatomic) CGFloat itemWidth;
@property (nonatomic) CGFloat miniContentHeight;
@end
@protocol CustomTabbarDelegate <NSObject>
- (BOOL)tabBar:(CustomTarbar *)tabBar shouldSelectItemAtIndex:(NSInteger)index;
- (void)tabBar:(CustomTarbar *)tabBar didSelectItemAtIndex:(NSInteger)index;
@end
//
// CustomTarbar.m
// CoolFrame
//
// Created by silicon on 2017/7/25.
// Copyright © 2017年 com.snailgames.coolframe. All rights reserved.
//
#import "CustomTarbar.h"
@implementation CustomTarbar
@synthesize delegate = _delegate;
@synthesize items = _items;
@synthesize selectedItem = _selectedItem;
@synthesize backgroundView = _backgroundView;
@synthesize miniContentHeight = _miniContentHeight;
@synthesize itemWidth = _itemWidth;
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if(self){
[self commonInitlization];
}
return self;
}
- (id)init{
return [self initWithFrame:CGRectZero];
}
- (void)commonInitlization{
self.backgroundView = [[UIView alloc] init];
[self addSubview:self.backgroundView];
}
- (void)layoutSubviews{
CGSize framesize = self.frame.size;
CGFloat height = self.miniContentHeight;
[self.backgroundView setFrame:CGRectMake(0, framesize.height - height, framesize.width, framesize.height)];
[self setItemWidth:roundf(framesize.width / self.items.count)];
int index = 0;
for (CustomTabBarItem *item in [self items]) {
CGFloat itemHeight = item.itemHeight;
if(!itemHeight){
itemHeight = framesize.height;
}
[item setFrame:CGRectMake(self.itemWidth * index, framesize.height - height, self.itemWidth, itemHeight)];
[item setNeedsDisplay];
index++;
}
}
#pragma mark - meathod
- (void)setItemWidth:(CGFloat)itemWidth{
if(itemWidth > 0){
_itemWidth = itemWidth;
}
}
- (void)setItems:(NSArray *)items{
_items = items;
for(CustomTabBarItem *item in items){
[item addTarget:self action:@selector(tabBarItemWasSelected:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:item];
}
}
- (CGFloat)miniContentHeight{
CGFloat minimumConentHeight = CGRectGetHeight(self.frame);
for (CustomTabBarItem *item in [self items]) {
CGFloat height = [item itemHeight];
if(height && height < minimumConentHeight){
minimumConentHeight = height;
}
}
return minimumConentHeight;
}
#pragma mark -Item selection
- (void)tabBarItemWasSelected:(id)sender{
if([[self delegate] respondsToSelector:@selector(tabBar:shouldSelectItemAtIndex:)]){
NSInteger index = [self.items indexOfObject:sender];
if(![[self delegate] tabBar:self shouldSelectItemAtIndex:index]){
return;
}
}
[self setSelectedItem:sender];
if([[self delegate] respondsToSelector:@selector(tabBar:didSelectItemAtIndex:)]){
NSInteger index = [self.items indexOfObject:self.selectedItem];
[[self delegate] tabBar:self didSelectItemAtIndex:index];
}
}
- (void)setSelectedItem:(CustomTabBarItem *)selectedItem{
if(selectedItem == _selectedItem){
return;
}
[_selectedItem setSelected:NO];
_selectedItem = selectedItem;
[_selectedItem setSelected:YES];
}
@end
到這裏,我們的開發進度差不多已經完成了 50% ,但是我們的 App 還不能夠順利的運行起來,因爲我們還缺一個視圖控制類,我們前面開發完成了:主要內容界面,按鈕以及存放按鈕的容器,但這些類別都是單獨存在的,我們需要一個組織者能夠把他們串起來,這就要求我們還要再開發一個控制器類,那該如何下手呢!其實很簡單,因爲我們的按鈕有自己的點擊事件,只要能夠告訴我們的控制器是哪個按鈕點擊了,那我們不就可以去切換那妞,控制去顯示主界面了嘛!這裏就要用到代理 Delegate 了( Ps: 你如果代碼看的比較多,就會發現這個模式簡直就是無處不再,太有用了), 觸發按鈕後通過代理,控制器類就會知道是哪一個按鈕被點擊了,很簡單直接看代碼:
#import <UIKit/UIKit.h>
#import "CustomTarbar.h"
@protocol CustomTarBarControllerDelegate;
@interface CustomTabBarController : UIViewController<CustomTabbarDelegate>
@property (nonatomic, strong) id<CustomTarBarControllerDelegate> delegate;
@property (nonatomic, strong) CustomTarbar *customTarbar;
@property (nonatomic, strong) NSMutableArray *viewControllers;
@property (nonatomic, strong) UIViewController *selectedViewController;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic) NSUInteger selectIndex;
@property (nonatomic) BOOL tabbarHidden;
- (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated;
@end
@protocol CustomTarBarControllerDelegate <NSObject>
@optional
- (BOOL)tabBarController:(CustomTabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;
- (void)tabBarController:(CustomTabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController;
@end
//
// CustomTabBarController.m
// CoolFrame
//
// Created by silicon on 2017/7/25.
// Copyright © 2017年 com.snailgames.coolframe. All rights reserved.
//
#import "CustomTabBarController.h"
#import <objc/runtime.h>
@interface CustomTabBarController ()
@end
@implementation CustomTabBarController
@synthesize delegate = _delegate;
@synthesize viewControllers = _viewControllers;
@synthesize selectIndex = _selectIndex;
@synthesize contentView = _contentView;
@synthesize customTarbar = _customTarbar;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"CoolFrame";
[self.view addSubview:[self contentView]];
[self.view addSubview:[self customTarbar]];
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self setSelectIndex:[self selectIndex]];
[[self customTarbar] setSelectedItem:[[self.customTarbar items] objectAtIndex:0]];
[self setTabBarHidden:NO animated:NO];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma -meathod
- (void)setViewControllers:(NSMutableArray *)viewControllers{
if(viewControllers && [viewControllers isKindOfClass:[NSMutableArray class]]){
_viewControllers = viewControllers;
NSMutableArray *tabBarItems = [[NSMutableArray alloc] init];
for (UIViewController *viewController in viewControllers) {
CustomTabBarItem *tabBarItem = [[CustomTabBarItem alloc] init];
[tabBarItem setTitle:viewController.title];
[tabBarItems addObject:tabBarItem];
}
[[self customTarbar] setItems:tabBarItems];
}
}
- (UIViewController *)selectedViewController{
return [self.viewControllers objectAtIndex:self.selectIndex];
}
- (void)setSelectIndex:(NSUInteger)selectIndex{
if(selectIndex > [self.viewControllers count]){
return;
}
_selectIndex = selectIndex;
if(_selectedViewController){
[_selectedViewController willMoveToParentViewController:nil];
[_selectedViewController.view removeFromSuperview];
[_selectedViewController removeFromParentViewController];
}
[self setSelectedViewController:[self.viewControllers objectAtIndex:_selectIndex]];
[self addChildViewController:self.selectedViewController];
[[self selectedViewController].view setFrame:self.contentView.bounds];
[self.contentView addSubview:self.selectedViewController.view];
[self.selectedViewController didMoveToParentViewController:self];
}
- (UIView *)contentView{
if(!_contentView){
_contentView = [[UIView alloc] init];
[_contentView setBackgroundColor:[UIColor whiteColor]];
[_contentView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
}
return _contentView;
}
- (CustomTarbar *)customTarbar{
if(!_customTarbar){
_customTarbar = [[CustomTarbar alloc] init];
[_customTarbar setBackgroundColor:[UIColor clearColor]];
[_customTarbar setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
_customTarbar.delegate = self;
}
return _customTarbar;
}
- (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated{
_tabbarHidden = hidden;
CGSize viewSize = self.view.frame.size;
CGFloat tabBarHeight = 49.0f;
CGFloat tabBarY = viewSize.height;
if(!hidden){
tabBarY = viewSize.height - tabBarHeight;
[[self customTarbar] setFrame:CGRectMake(0, tabBarY, viewSize.width, tabBarHeight)];
[[self contentView] setFrame:CGRectMake(0, 0, viewSize.width, viewSize.height - tabBarHeight)];
}
}
- (void)setTabbarHidden:(BOOL)tabbarHidden{
[self setTabBarHidden:tabbarHidden animated:NO];
}
#pragma -CustomTabbarDelegate
- (BOOL)tabBar:(CustomTarbar *)tabBar shouldSelectItemAtIndex:(NSInteger)index{
if (index > [self viewControllers].count) {
return NO;
}
if([[self delegate] respondsToSelector:@selector(tabBarController:shouldSelectViewController:)]){
if(![[self delegate] tabBarController:self shouldSelectViewController:[self viewControllers][index]]){
return NO;
}
}
if([self selectedViewController] == [self viewControllers][index]){
if([[self selectedViewController] isKindOfClass:[UINavigationController class]]){
UINavigationController *selectController = (UINavigationController *)[self selectedViewController];
if([selectController topViewController] != [selectController viewControllers][0]){
[selectController popToRootViewControllerAnimated:YES];
return NO;
}
}
return NO;
}
return YES;
}
- (void)tabBar:(CustomTarbar *)tabBar didSelectItemAtIndex:(NSInteger)index{
if(index < 0 || index >= [self viewControllers].count){
return;
}
[self setSelectIndex:index];
}
@end
到這裏,還缺一個導航欄,缺了咋辦,咱們來給他加上不就完事了嘛!首先定義一個類繼承自 UINavigationBar ,這個類我用於控制導航欄的大小,再創建一個繼承自UINavigationController 的類,控制我們的界面爲豎屏,具體的代碼如下:
#import "CustomNavBar.h"
@implementation CustomNavBar{
CGSize _previousSize;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
- (CGSize)sizeThatFits:(CGSize)size{
size = [super sizeThatFits:size];
if([UIApplication sharedApplication].statusBarHidden){
size.height = 64;
}
return size;
}
- (void)layoutSubviews{
[super layoutSubviews];
if(CGSizeEqualToSize(self.bounds.size, _previousSize)){
_previousSize = self.bounds.size;
[self.layer removeAllAnimations];
[self.layer.sublayers makeObjectsPerformSelector:@selector(removeAllAnimations)];
}
}
@end
#import "CustomNavigationController.h"
@implementation CustomNavigationController
- (BOOL)shouldAutorotate{
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationPortrait;
}
@end
最後,把該需要的資源文件都導入到工程目錄中來,在我們的AppDelegate 中設置好,AppDelegate 代碼如下:
#import "AppDelegate.h"
#import "CustomNavigationController.h"
#import "CustomNavBar.h"
#import "FirstViewController.h"
#import "SecondViewController.h"
#import "ThirdViewController.h"
#import "FouthViewController.h"
#define NQFONT(v) [UIFont fontWithName:@"HiraKakuProN-W3" size:v]
#define RGB(r, g, b) [UIColor colorWithRed:(r)/255.f green:(g)/255.f blue:(b)/255.f alpha:1.f]
@interface AppDelegate ()
@property (nonatomic, strong) FirstViewController *firstViewController;
@property (nonatomic, strong) SecondViewController *secondViewController;
@property (nonatomic, strong) ThirdViewController *thirdViewController;
@property (nonatomic, strong) FouthViewController *fouthViewController;
@property (nonatomic, strong) CustomTabBarController *tabBarController;
@property (nonatomic, strong) CustomNavigationController *navController;
@end
@implementation AppDelegate
- (void)setupViewControllers{
if(!self.firstViewController){
self.firstViewController = [[FirstViewController alloc] init];
}
if(!self.secondViewController){
self.secondViewController = [[SecondViewController alloc] init];
}
if(!self.thirdViewController){
self.thirdViewController = [[ThirdViewController alloc] init];
}
if(!self.fouthViewController){
self.fouthViewController = [[FouthViewController alloc] init];
}
self.tabBarController = [[CustomTabBarController alloc] init];
NSMutableArray *viewsArray = [[NSMutableArray alloc] initWithObjects:self.firstViewController,
self.secondViewController,
self.thirdViewController,
self.fouthViewController, nil];
[self.tabBarController setViewControllers:viewsArray];
[self.tabBarController setSelectIndex:0];
self.tabBarController.delegate = self;
[self customizeTabBarForController:_tabBarController];
if(!_navController){
_navController = [[CustomNavigationController new] initWithNavigationBarClass:[CustomNavBar class] toolbarClass:[UIToolbar class]];
[_navController pushViewController:_tabBarController animated:NO];
}
}
- (void)customizeTabBarForController:(CustomTabBarController *)tabBarController{
UIImage *finishedImage = [UIImage imageNamed:@"tabbar_back_selected"];
UIImage *unfinishedImage = [UIImage imageNamed:@"tabbar_back_normal"];
NSArray *tabBarItemImages = @[@"latest",@"rank", @"contest", @"me"];
NSArray *tabBarItemTitles = @[NSLocalizedString(@"新聞", nil),NSLocalizedString(@"直播", nil),NSLocalizedString(@"發現", nil), NSLocalizedString(@"我", nil)];
NSInteger index = 0;
for (CustomTabBarItem *item in [[tabBarController customTarbar] items])
{
[item setBackgroundSelectedImage:finishedImage withUnselectedImage:unfinishedImage];
UIImage *selectedimage = [UIImage imageNamed:[tabBarItemImages objectAtIndex:index]];
UIImage *unselectedimage = [UIImage imageNamed:[tabBarItemImages objectAtIndex:index]];
[item setFinishedSelectedImage:selectedimage withFinishedUnselectedImage:unselectedimage];
[item setTitle:[tabBarItemTitles objectAtIndex:index]];
item.unselectedTitleAttributes= @{NSFontAttributeName: NQFONT(10), NSForegroundColorAttributeName: RGB(255, 255, 255),};
item.selectedTitleAttributes = @{NSFontAttributeName: NQFONT(10), NSForegroundColorAttributeName: RGB(255, 255, 255),};
index++;
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window setBackgroundColor:[UIColor whiteColor]];
[self setupViewControllers];
[self.window setRootViewController:_navController];
[self.window makeKeyAndVisible];
return YES;
}
到這裏,我們的開發就算完成了,一個簡單的 App 界面框架就誕生了,在接下來的幾篇文章中,我會對其不斷的完善,加上更多新的東西,讓其成爲一個真正意義上的 App。
好了。祝大家生活愉快。多多收穫友誼和愛情。如果想獲取更多的訊息,請掃描下方二維碼關注我的微信公衆號: