那麼我們的設置界面到底要怎麼寫才能最方便使用呢?下面我就來說說我的想法。
1.觀察原型圖
2.找出相同的東西,目的是抽出一個基類模塊,只要我寫好了這個控制器,其它的界面全部都寫好了
3.判斷是用純代碼還是storyboard,如果界面的控件位置都是固定,用storyboard。
什麼時候用靜態單元格:如果界面cell跟系統自帶的cell完全差不多,就可以選擇靜態單元格
如果不相似:
1.判斷其它cell是否全部差不多,如果全部差不多,可以使用靜態單元格
2.如果界面差別很大,就不使用靜態單元格,就用代碼
那麼下面我們就來抽取設置界面:
首先繼承UITableViewController寫一個BasicSettingViewController
.h
//這一組有多少行cell<SettingItem>
@property (nonatomic, strong) NSMutableArray *groups;
.m
#import "BasicTableViewController.h"
#import "GroupItem.h"
#import "SettingItem.h"
#import "BasicCell.h"
@interface BasicTableViewController ()
@end
@implementation BasicTableViewController
//讓這個類一初始化就是組樣式的
- (instancetype)init {
return [self initWithStyle:UITableViewStyleGrouped];
}
- (void)viewDidLoad {
[super viewDidLoad];
//設置tableView的一些基本信息
self.tableView.backgroundColor = [UIColor colorWithRed:211 green:211 blue:211 alpha:1];
self.tableView.sectionHeaderHeight = 10;
self.tableView.sectionFooterHeight = 0;
self.tableView.contentInset = UIEdgeInsetsMake(-25, 0, 0, 0);
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}
//一定要對組進行懶加載,不然的話,子類是沒有辦法初始化他的
- (NSMutableArray *)groups {
if (!_groups) {
_groups = [NSMutableArray array];
}
return _groups;
}
#pragma mark - Table view data source
//返回組數
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Incomplete implementation, return the number of sections
return self.groups.count;
}
//返回行數
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#warning Incomplete implementation, return the number of rows
GroupItem *group = self.groups[section];
return group.items.count;
}
//初始化cell並給cell賦值
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
BasicCell *cell = [BasicCell cellWithTableView:tableView];
GroupItem *group = self.groups[indexPath.section];
SettingItem *item = group.items[indexPath.row];
cell.item = item;
[cell setIndexPath:indexPath rowCount:group.items.count];
return cell;
}
//返回腳部標題
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
GroupItem *group = self.groups[section];
return group.footTitle;
}
//返回頭部標題
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
GroupItem *group = self.groups[section];
return group.headTitle;
}
//當cell選中的時候執行該方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
GroupItem *group = self.groups[indexPath.section];
SettingItem *item = group.items[indexPath.row];
if (item.destVc) {//如果有跳轉就執行跳轉
UIViewController *destVc = [[item.destVc alloc] init];
[self.navigationController pushViewController:destVc animated:YES];
}
if (item.option) {//哪個cell有事情就做事情(自己的事情自己干)
item.option();
}
}
復制代碼
設置組模型(基類):
復制代碼
@interface GroupItem : NSObject
//每一組的行數
@property (nonatomic, strong) NSArray *items;
//頭標題
@property (nonatomic, copy) NSString *headTitle;
//腳標題
@property (nonatomic, copy) NSString *footTitle;
@end
typedef void (^SettingItemOption) ();
@interface SettingItem : NSObject
//左邊神圖
@property (nonatomic, strong) UIImage *image;
//標題
@property (nonatomic, copy) NSString *title;
//子標題
@property (nonatomic, copy) NSString *subTitle;
@property (nonatomic, assign) Class destVc;//跳轉到目標控制器(最好不要用NSString)
@property (nonatomic, copy) SettingItemOption option;//在ARC模型下只能使用copy,MRC可以使用copy或者strong,為了把他放到堆內存中,如果放在棧內存中會被釋放
//提供兩個創建的item的方法
+ (instancetype)itemWithTitle:(NSString *)title;
+ (instancetype)itemWithTitle:(NSString *)title image:(UIImage *)image;
@end
因為內容是根據模型的改變而改變,那麼我們就創建幾個模型(繼承於SettingItem):
每個基類要是有自己特有的一些屬性也可以設置
@interface ArrowItem : SettingItem
@end
@interface BadgeItem : SettingItem
@property (nonatomic, copy) NSString *badgeValue;
@end
@interface SwitchItem : SettingItem
@property (nonatomic, assign) BOOL isOn;//是否打開
@end
@interface LabelItem : SettingItem
@property (nonatomic, copy) NSString *text;//label上面的text
@end
@interface CheakItem : SettingItem
@property (nonatomic, assign) BOOL isCheak;//是否打勾
@end
設置的cell(基類):
復制代碼
@class SettingItem;
@interface BasicCell : UITableViewCell
//根據settingItem模型來顯示內容
@property (nonatomic, strong) SettingItem *item;
//給外部提供一個創建cell的方法
+ (instancetype)cellWithTableView:(UITableView *)tableView;
//設置每一組的背景圖片的方法
- (void)setIndexPath:(NSIndexPath *)indexPath rowCount:(NSInteger)rowCount;
@end
@interface BasicCell ()
//這裡一定要用strong,因為是加在視圖上面而不是加在contentView;
//箭頭格式的cell
@property (nonatomic, strong) UIImageView *arrowView;
//開關格式的cell
@property (nonatomic, strong) UISwitch *switchView;
//打勾格式的cell
@property (nonatomic, strong) UIImageView *cheakView;
//label格式的cell
@property (nonatomic, weak) UILabel *labelView;
@end
//不變形的拉伸圖片(一般要是寫項目最好寫成category)
@implementation UIImage (Resizable)
+ (UIImage *)resizableWithImageName:(NSString *)imageName {
UIImage *image = [UIImage imageNamed:imageName];
return [image stretchableImageWithLeftCapWidth:image.size.width/2 topCapHeight:image.size.height/2];
}
@end
@implementation BasicCell
#pragma mark - 對所有的控件懶加載
- (UIImageView *)arrowView
{
if (_arrowView == nil) {
_arrowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"common_icon_arrow"]];
}
return _arrowView;
}
- (UISwitch *)switchView
{
if (_switchView == nil) {
_switchView = [[UISwitch alloc] init];
[_switchView addTarget:self action:@selector(switchChange:) forControlEvents:UIControlEventValueChanged];
}
return _switchView;
}
- (UIImageView *)cheakView
{
if (_cheakView == nil) {
_cheakView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"common_icon_checkmark"]];
}
return _cheakView;
}
- (UILabel *)labelView {
if (!_labelView) {
UILabel *labelView = [[UILabel alloc] initWithFrame:self.bounds];
labelView.textAlignment = NSTextAlignmentCenter;
labelView.textColor = [UIColor redColor];
[self.contentView addSubview:labelView];
_labelView = labelView;
}
return _labelView;
}
#pragma mark - switchChange
- (void)switchChange:(UISwitch *)switchView {
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
//只需要設置一次的東西最好放到這個方法中執行,因為這個方法只會在創建cell的時候執行一次,而不會每次給模型賦值的時候都會執行
self.detailTextLabel.font = [UIFont systemFontOfSize:14];
self.backgroundColor = [UIColor clearColor];
self.backgroundView = [[UIImageView alloc] init];
self.selectedBackgroundView = [[UIImageView alloc] init];
}
return self;
}
+ (instancetype)cellWithTableView:(UITableView *)tableView {
static NSString *ID = @"cell";
BasicCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
//這裡一定要用self
cell = [[self alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ID];
}
return cell;
}
- (void)setItem:(SettingItem *)item {
_item = item;
//設置數據
[self setUpData];
//設置模型
[self setUpRightView];
}
- (void)setUpData {
self.textLabel.text = _item.title;
self.detailTextLabel.text = _item.subTitle;
self.imageView.image = _item.image;
}
- (void)setUpRightView {
if ([_item isKindOfClass:[ArrowItem class]]) {
//箭頭
self.accessoryView = self.arrowView;
}
else if ([_item isKindOfClass:[BadgeItem class]]) {
//badgeView
self.accessoryView = self.badgeView;
}
else if ([_item isKindOfClass:[SwitchItem class]]) {
//開關
self.accessoryView = self.switchView;
}
else if ([_item isKindOfClass:[LabelItem class]]) {
//Label
LabelItem *labelItem = (LabelItem *)_item;
UILabel *label = self.labelView;
label.text = labelItem.text;
}
else if ([_item isKindOfClass:[CheakItem class]]) {
//打勾
CheakItem *cheakItem = (CheakItem *)_item;
if (cheakItem.isCheak) {
self.accessoryView = self.cheakView;
}
else {
self.accessoryView = nil;
}
}
else {
[_labelView removeFromSuperview];
_labelView = nil;
self.accessoryView = nil;
}
}
- (void)setIndexPath:(NSIndexPath *)indexPath rowCount:(NSInteger)rowCount {
UIImageView *bgView = (UIImageView *)self.backgroundView;
UIImageView *selBgView = (UIImageView *)self.selectedBackgroundView;
if (rowCount == 1) { // 只有一行
bgView.image = [UIImage resizableWithImageName:@"common_card_background"];
selBgView.image = [UIImage resizableWithImageName:@"common_card_background_highlighted"];
}else if(indexPath.row == 0){ // 頂部cell
bgView.image = [UIImage resizableWithImageName:@"common_card_top_background"];
selBgView.image = [UIImage resizableWithImageName:@"common_card_top_background_highlighted"];
}else if (indexPath.row == rowCount - 1){ // 底部
bgView.image = [UIImage resizableWithImageName:@"common_card_bottom_background"];
selBgView.image = [UIImage resizableWithImageName:@"common_card_bottom_background_highlighted"];
}else{ // 中間
bgView.image = [UIImage resizableWithImageName:@"common_card_middle_background"];
selBgView.image = [UIImage resizableWithImageName:@"common_card_middle_background_highlighted"];
}
}
#pragma mark - 這裡寫才能居中對齊
- (void)layoutSubviews {
[super layoutSubviews];
_labelView.frame = self.bounds;
}
到此,所有的都已經抽出來了。你可能想問了那要麼是子類和父類長的很像但是卻不一樣怎麼辦?那麼就能用到OC中繼承的這個性質了,子類只要重寫一下父類的方法就可以了。
隨便寫幾個控制器,繼承於BasicSettingViewController
我的界面
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setUpNav];
[self setUpGroup0];
[self setUpGroup1];
[self setUpGroup2];
[self setUpGroup3];
}
- (void)setUpNav {
UIBarButtonItem *setting = [[UIBarButtonItem alloc] initWithTitle:@"設置" style:UIBarButtonItemStylePlain target:self action:@selector(setting)];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSForegroundColorAttributeName] = [UIColor blackColor];
[setting setTitleTextAttributes:dict forState:UIControlStateNormal];
self.navigationItem.rightBarButtonItem = setting;
}
#pragma mark - 設置
- (void)setting {
SettingViewController *setVc = [[SettingViewController alloc] init];
[self.navigationController pushViewController:setVc animated:YES];
}
- (void)setUpGroup0
{
// 新的好友
ArrowItem *friend = [ArrowItem itemWithTitle:@"新的好友" image:[UIImage imageNamed:@"new_friend"]];
GroupItem *group = [[GroupItem alloc] init];
group.items = @[friend];
[self.groups addObject:group];
}
- (void)setUpGroup1
{
// 我的相冊
ArrowItem *album = [ArrowItem itemWithTitle:@"我的相冊" image:[UIImage imageNamed:@"album"]];
album.subTitle = @"(12)";
// 我的收藏
ArrowItem *collect = [ArrowItem itemWithTitle:@"我的收藏" image:[UIImage imageNamed:@"collect"]];
collect.subTitle = @"(0)";
// 贊
ArrowItem *like = [ArrowItem itemWithTitle:@"贊" image:[UIImage imageNamed:@"like"]];
like.subTitle = @"(0)";
GroupItem *group = [[GroupItem alloc] init];
group.items = @[album,collect,like];
[self.groups addObject:group];
}
- (void)setUpGroup2{
// 微博支付
ArrowItem *pay = [ArrowItem itemWithTitle:@"微博支付" image:[UIImage imageNamed:@"pay"]];
// 個性化
ArrowItem *vip = [ArrowItem itemWithTitle:@"個性化" image:[UIImage imageNamed:@"vip"]];
vip.subTitle = @"微博來源、皮膚、封面圖";
GroupItem *group = [[GroupItem alloc] init];
group.items = @[pay,vip];
[self.groups addObject:group];
}
- (void)setUpGroup3
{
// 我的二維碼
ArrowItem *card = [ArrowItem itemWithTitle:@"我的二維碼" image:[UIImage imageNamed:@"card"]];
// 草稿箱
ArrowItem *draft = [ArrowItem itemWithTitle:@"草稿箱" image:[UIImage imageNamed:@"draft"]];
GroupItem *group = [[GroupItem alloc] init];
group.items = @[card,draft];
[self.groups addObject:group];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ProfileCell *cell = [ProfileCell cellWithTableView:tableView];
GroupItem *group = self.groups[indexPath.section];
SettingItem *item = group.items[indexPath.row];
cell.item = item;
[cell setIndexPath:indexPath rowCount:group.items.count];
return cell;
}
復制代碼
因為cell和父類不太一樣,所以cell要繼承於BasicCell重寫一下
復制代碼
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
self.detailTextLabel.font = [UIFont systemFontOfSize:12];
}
return self;
}
//一定要在這個方法中重寫,不然可能會沒用,這個是在加載完成即將顯示的時候調用,加載的frame是100%正確的
- (void)layoutSubviews {
[super layoutSubviews];
CGRect frame = self.detailTextLabel.frame;
frame.origin.x = CGRectGetMaxX(self.textLabel.frame) + 5;
self.detailTextLabel.frame = frame;
}
設置界面
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 添加第0組
[self setUpGroup0];
// 添加第1組
[self setUpGroup1];
// 添加第2組
[self setUpGroup2];
// 添加第3組
[self setUpGroup3];
}
- (void)setUpGroup0
{
// 賬號管理
ArrowItem *account = [ArrowItem itemWithTitle:@"賬號管理"];
// account.badgeValue = @"8";
GroupItem *group = [[GroupItem alloc] init];
group.items = @[account];
[self.groups addObject:group];
}
- (void)setUpGroup1
{
// 提醒和通知
ArrowItem *notice = [ArrowItem itemWithTitle:@"我的相冊" ];
// 通用設置
ArrowItem *setting = [ArrowItem itemWithTitle:@"通用設置" ];
setting.destVc = [CommonViewController class];
// 隱私與安全
ArrowItem *secure = [ArrowItem itemWithTitle:@"隱私與安全" ];
GroupItem *group = [[GroupItem alloc] init];
group.items = @[notice,setting,secure];
[self.groups addObject:group];
}
- (void)setUpGroup2{
// 意見反饋
ArrowItem *suggest = [ArrowItem itemWithTitle:@"意見反饋" ];
// 關於微博
ArrowItem *about = [ArrowItem itemWithTitle:@"關於微博"];
GroupItem *group = [[GroupItem alloc] init];
group.items = @[suggest,about];
[self.groups addObject:group];
}
- (void)setUpGroup3
{
// 賬號管理
LabelItem *layout = [[LabelItem alloc] init];
layout.text = @"退出當前賬號";
GroupItem *group = [[GroupItem alloc] init];
group.items = @[layout];
[self.groups addObject:group];
}
通用設置界面
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 添加第0組
[self setUpGroup0];
// 添加第1組
[self setUpGroup1];
// 添加第2組
[self setUpGroup2];
// 添加第3組
[self setUpGroup3];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refresh:) name:@"FontSizeChangeNote" object:nil];
}
- (void)refresh:(NSNotification *)notification {
_fontSize.subTitle = notification.userInfo[FontSizeKey];
[self.tableView reloadData];
}
- (void)setUpGroup0
{
// 閱讀模式
SettingItem *read = [SettingItem itemWithTitle:@"閱讀模式"];
read.subTitle = @"有圖模式";
// 字體大小
SettingItem *fontSize = [SettingItem itemWithTitle:@"字體大小"];
_fontSize = fontSize;
NSString *fontSizeStr = [[NSUserDefaults standardUserDefaults] objectForKey:FontSizeKey];
if (fontSizeStr == nil) {
fontSizeStr = @"中";
}
fontSize.subTitle = fontSizeStr;
fontSize.destVc = [FontViewController class];
// 顯示備注
SwitchItem *remark = [SwitchItem itemWithTitle:@"顯示備注"];
GroupItem *group = [[GroupItem alloc] init];
group.items = @[read,fontSize,remark];
[self.groups addObject:group];
}
- (void)setUpGroup1
{
// 圖片質量
ArrowItem *quality = [ArrowItem itemWithTitle:@"圖片質量" ];
GroupItem *group = [[GroupItem alloc] init];
group.items = @[quality];
[self.groups addObject:group];
}
- (void)setUpGroup2{
// 聲音
SwitchItem *sound = [SwitchItem itemWithTitle:@"聲音" ];
GroupItem *group = [[GroupItem alloc] init];
group.items = @[sound];
[self.groups addObject:group];
}
- (void)setUpGroup3
{
// 多語言環境
SettingItem *language = [SettingItem itemWithTitle:@"多語言環境"];
language.subTitle = @"跟隨系統";
GroupItem *group = [[GroupItem alloc] init];
group.items = @[language];
[self.groups addObject:group];
}
字體大小界面
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setUpGroup0];
}
- (void)setUpGroup0
{
#pragma mark - 這種方法能消除block中的循環警告
// 大
__weak typeof(self) weakSelf = self;
CheakItem * __weak big = [CheakItem itemWithTitle:@"大"];
big.option = ^{
[weakSelf selItem:big];
};
// 中
CheakItem * __weak middle = [CheakItem itemWithTitle:@"中"];
middle.option = ^{
[weakSelf selItem:middle];
};
_selCheakItem = middle;
// 小
CheakItem * __weak small = [CheakItem itemWithTitle:@"小"];
small.option = ^{
[weakSelf selItem:small];
};
GroupItem *group = [[GroupItem alloc] init];
group.headTitle = @"上傳圖片質量";
group.items = @[big,middle,small];
[self.groups addObject:group];
// 默認選中item
[self setUpSelItem:middle];
}
- (void)setUpSelItem:(CheakItem *)item
{
NSString *fontSizeStr = [[NSUserDefaults standardUserDefaults] objectForKey:FontSizeKey];
if (fontSizeStr == nil) {
[self selItem:item];
return;
}
for (GroupItem *group in self.groups) {
for (CheakItem *item in group.items) {
if ( [item.title isEqualToString:fontSizeStr]) {
[self selItem:item];
}
}
}
}
- (void)selItem:(CheakItem *)item
{
_selCheakItem.isCheak = NO;
item.isCheak = YES;
_selCheakItem = item;
[self.tableView reloadData];
// 存儲
[[NSUserDefaults standardUserDefaults] setObject:item.title forKey:FontSizeKey];
[[NSUserDefaults standardUserDefaults] synchronize];
// 發出通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"FontSizeChangeNote" object:nil userInfo:@{FontSizeKey:item.title}];
}