[iOS]MVVM
阿新 • • 發佈:2018-12-12
demo:https://download.csdn.net/download/u012881779/10696246 之前使用MVC模式開發,覺得還蠻好用就一直使用著。 最近接觸MVVM比較頻繁,發現相比於MVC它會將網路請求從控制器中分離出來,這樣能有效的為ViewController瘦身。 今天有點時間,就呼叫高德地圖獲取地區的介面寫了一個分層選擇地區的demo。 結構: Controller
#import "GADistrictsViewController.h" #import "GADistrictsCell.h" #import "GADistrictsModel.h" #import "GADistrictsViewModel.h" @interface GADistrictsViewController () <UITableViewDelegate, UITableViewDataSource> @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (strong, nonatomic) NSMutableArray *dataMArr; @property (strong, nonatomic) GADistrictsViewModel *viewModel; @end @implementation GADistrictsViewController - (void)viewDidLoad { [super viewDidLoad]; [_tableView setSeparatorStyle:NO]; _tableView.estimatedRowHeight = 100; _tableView.rowHeight = UITableViewAutomaticDimension; if (!_viewModel) { _viewModel = [GADistrictsViewModel new]; } @weakify(self) NSMutableDictionary *exeDict = [NSMutableDictionary new]; [exeDict setObject:@"bf4783277065f752f68490c4bf2b79e0" forKey:@"key"]; [exeDict setObject:@"3" forKey:@"subdistrict"]; [[_viewModel.districtsCommand execute:exeDict] subscribeNext:^(id _Nullable result) { //NSLog(@"%@",result); GADistrictsModel *tempModel = (GADistrictsModel *)[((GAResultDataModel *)result).districts firstObject]; self_weak_.dataMArr = tempModel.districts; [self_weak_.tableView reloadData]; } error:^(NSError * _Nullable error) { }]; } #pragma mark UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return _dataMArr.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { GADistrictsModel *sectionModel = [_dataMArr objectAtIndex:section]; if (_dataMArr.count > section) { sectionModel = [_dataMArr objectAtIndex:section]; NSInteger newIndex = 0; if (sectionModel.select) { for (int i = 0 ; i < sectionModel.districts.count ; i ++) { GADistrictsModel *iModel = [sectionModel.districts objectAtIndex:i]; newIndex = newIndex + 1; if (iModel.select) { for (int j = 0 ; j < iModel.districts.count ; j ++) { //GADistrictsModel *jModel = [iModel.districts objectAtIndex:j]; newIndex = newIndex + 1; } } } return newIndex+1; } } return 1; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { GADistrictsModel *sectionModel = [_dataMArr objectAtIndex:indexPath.section]; if (_dataMArr.count > indexPath.section) { sectionModel = [_dataMArr objectAtIndex:indexPath.section]; } GADistrictsModel *model; if (sectionModel.select) { if (indexPath.row == 0) { model = sectionModel; } else { NSInteger newIndex = 0; for (int i = 0 ; i < sectionModel.districts.count ; i ++) { GADistrictsModel *iModel = [sectionModel.districts objectAtIndex:i]; newIndex = newIndex + 1; if (indexPath.row == newIndex) { model = iModel; break; } if (iModel.select) { for (int j = 0 ; j < iModel.districts.count ; j ++) { GADistrictsModel *jModel = [iModel.districts objectAtIndex:j]; newIndex = newIndex + 1; if (indexPath.row == newIndex) { model = jModel; break; } } } } } } else { model = sectionModel; } if ([[model class] isSubclassOfClass:[GADistrictsModel class]]) { GADistrictsCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GADistrictsCell"]; if (!cell) { cell = [[[NSBundle mainBundle] loadNibNamed:@"GADistrictsCell" owner:nil options:nil] objectAtIndex:0]; cell.selectionStyle = UITableViewCellSelectionStyleNone; [cell setBackgroundColor:[UIColor clearColor]]; } cell.model = model; [cell.nameBut setTitle:model.name forState:UIControlStateNormal]; cell.codeLab.text = [model.adcode stringValue]; if ([model.level isEqualToString:@"province"]) { cell.nameBut.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0); [cell.nameBut setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; cell.codeLab.textColor = [UIColor blueColor]; } else if ([model.level isEqualToString:@"city"]) { cell.nameBut.contentEdgeInsets = UIEdgeInsetsMake(0, 32, 0, 0); [cell.nameBut setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; cell.codeLab.textColor = [UIColor blackColor]; } else if ([model.level isEqualToString:@"district"] || [model.level isEqualToString:@"street"]) { cell.nameBut.contentEdgeInsets = UIEdgeInsetsMake(0, 64, 0, 0); [cell.nameBut setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal]; cell.codeLab.textColor = [UIColor lightGrayColor]; } return cell; } UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:0 reuseIdentifier:@"UITableViewCell"]; } return cell; } #pragma mark UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { GADistrictsCell *cell = [tableView cellForRowAtIndexPath:indexPath]; GADistrictsModel *model = cell.model; if (model) { model.select = !model.select; [_tableView reloadData]; } } @end
Cell
#import <UIKit/UIKit.h>
#import "GADistrictsModel.h"
@interface GADistrictsCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UIButton *nameBut;
@property (weak, nonatomic) IBOutlet UILabel *codeLab;
@property (weak, nonatomic) GADistrictsModel *model;
@end
Model
#import "JSONModel.h" /** @protocol GADistrictsModel這個協議是必須要新增的,而且要加在@interface GAResultDataModel之前。 不然在GAResultDataModel中初始化districts欄位時,就係統會要求這樣寫“NSArray <GADistrictsModel *> *districts”。 這種寫法存在問題,它並不會將陣列中的NSDictionary初始化為GADistrictsModel。 */ @protocol GADistrictsModel @end @interface GAResultDataModel : JSONModel @property (strong, nonatomic) NSNumber *newcount; @property (assign, nonatomic) BOOL info; @property (assign, nonatomic) NSInteger infocode; @property (strong, nonatomic) NSString *status; // "Optional"可選屬性 (就是說這個屬性可以為null或者為空) @property (strong, nonatomic) NSDictionary <Optional> *suggestion; @property (strong, nonatomic) NSArray <GADistrictsModel> *districts; @end @interface GADistrictsModel : JSONModel @property (strong, nonatomic) NSNumber *adcode; @property (strong, nonatomic) NSString *center; @property (strong, nonatomic) NSString *level; @property (strong, nonatomic) NSString *name; // "Ignore"忽略屬性 (就是完全忽略這個屬性)。應為在國家那一級這個欄位返回的陣列型別,在城市那一級這個欄位返回的字串型別。如果不能忽略就需要再單獨建立一個model。 @property (strong, nonatomic) NSString <Ignore> *citycode; @property (strong, nonatomic) NSArray <GADistrictsModel>*districts; @property (assign, nonatomic) BOOL select; @end #import "GADistrictsModel.h" @implementation GAResultDataModel // 設定所有的屬性為可選(所有屬性值可以為空) + (BOOL)propertyIsOptional:(NSString *)propertyName { return YES; } // key對映 + (JSONKeyMapper *)keyMapper { return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"newcount": @"count" }]; } @end @implementation GADistrictsModel + (BOOL)propertyIsOptional:(NSString *)propertyName { return YES; } @end
ViewModel
#import <Foundation/Foundation.h> #import "ReactiveObjC.h" @interface GADistrictsViewModel : NSObject // 獲取地區 @property (nonatomic,strong) RACCommand *districtsCommand; @end #import "GADistrictsViewModel.h" #import "GADistrictsAPI.h" #import "GADistrictsModel.h" @implementation GADistrictsViewModel - (RACCommand *)districtsCommand { if (!_districtsCommand) { _districtsCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) { return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [GADistrictsAPI aMapDistrictsWithParameters:input Success:^(id result) { GAResultDataModel *tempResultModel = [[GAResultDataModel alloc] initWithDictionary:result error:nil]; // 傳送訊號 [subscriber sendNext:tempResultModel]; [subscriber sendCompleted]; } failed:^(NSError *error) { [subscriber sendError:error]; }]; return nil; }]; }]; } return _districtsCommand; } @end
API
#import <Foundation/Foundation.h>
#import "AFNetworking.h"
typedef void (^SuccessBlock)(id result);
typedef void (^FailedBlock)(NSError *error);
@interface GADistrictsAPI : NSObject
/// 高德地圖-獲取省市縣
+ (void)aMapDistrictsWithParameters:(NSDictionary *)parameter Success:(SuccessBlock)success failed:(FailedBlock)failed;
@end
#import "GADistrictsAPI.h"
@implementation GADistrictsAPI
+ (void)aMapDistrictsWithParameters:(NSDictionary *)parameter Success:(SuccessBlock)success failed:(FailedBlock)failed {
NSString *tempUrl = [NSString stringWithFormat:@"https://restapi.amap.com/v3/config/district?key=%@&subdistrict=%@",[parameter objectForKey:@"key"],[parameter objectForKey:@"subdistrict"]];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:tempUrl parameters:[NSMutableDictionary dictionary] progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
failed(error);
}];
}
@end
示意圖: