2016-05-30 15:25:38 zhonggaorong 阅读数 3498
  • Microsoft Blend 2019/C#/.NET/MVVM/WCF/XAML/WPF/UWP

    本课程主要讲解Microsoft Blend For Visual Studio 2019,帮你一步步掌握Microsoft Blend的那令人激动的超强功能。如果你正在使用WPF、UWP技术进行开发,你一定需要学习Microsoft Blend,在这里你会发现,只需动动鼠标,你就可以迅速开发出又酷、又炫、又华丽应用程序界面,而系统自动为你生成全部的XAML代码。从此告别手工编写XAML的苦逼日子

    2705 人正在学习 去看看 赵庆明

MVC开发模式 : 
1. 苹果官方一直推荐我们开发者使用MVC的开发模式,所以我们大部分人之前的项目都是用MVC来开发APP,这样开发,肯定会发现一个超级大的弊端,viewcontroller里边有大量的业务逻辑与视图操作逻辑,随着项目的不断的迭代,会充斥着大量的问题,我们的单元测试也好, 我们的逻辑设计,以及代码的整洁性,代码的层级性都会出现很多的问题,为此我觉得为Controller瘦身已经是非常必要的。  也有一部分开发者着不同意这种思想,但是我们也不必固守成规,虽然MVVM想必与MVC可能从代码上来讲并没有减少,反而可能增加。 但是相对应的,也会带来很多好处,下面就是MVVM 使用后的一些优点。


MVVM的优点如下:

MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有以下四大优点
1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。


下面通过代码来进行来进行说明

1.首先项目的层级关系图。


1.model文件夹: 这个和mvc下的model没有什么区别。 一般为瘦model. 

2. view文件夹:  视图的载体, 和mvc下的view也没有什么区别, 由controller进行协调展示。

3. Controller文件夹 :  只包含一些视图的操作,不包含任何的业务处理。这里要达到瘦身的效果,经常会吧tableview 的protocal给单独提出来,本文章将的也会把这些类给提出来,后面详细说明

4.ViewModel:  把以前充斥在Controller中的一些业务处理放置在此地, 包含:数据请求,数据的包装。把封装好的数据直接传递给Controller直接进行显示。 网络请求的起飞与着陆点,这个看项目的网络层架构设计。 不能一视同仁

5.TableViewProtocol文件夹:  这个就是把Tableview的数据源与delegate给提取出来,这样做的好处就是可以最大化的重用对应的代理方法,易于管理, 并且可以让controller中及其的简洁。

6.3rdLibs文件夹: 存放了一个第三方的上下拉加载控件。


2.完整的项目代码结构目录:



下面让我们一级一级的进行代码的解剖:

1.Controller代码解剖:

TableViewController.h

#import <UIKit/UIKit.h>
@interface TableViewController : UIViewController

@end

TableViewController.m

#import "TableViewController.h"
#import "YiRefreshHeader.h"
#import "YiRefreshFooter.h"
#import "TableViewModel.h"
#import "TableViewDataSource.h"
#import "TableViewDelegate.h"
@interface TableViewController ()
{
    
    YiRefreshHeader *refreshHeader;
    YiRefreshFooter *refreshFooter;
    NSMutableArray *totalSource;
    TableViewModel *tableViewModel;
    UITableView *tableView;
    TableViewDataSource *tableViewDataSource;
    TableViewDelegate *tableViewDelegate;
}

@end

@implementation TableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    if (iOS7) {
        self.edgesForExtendedLayout = UIRectEdgeBottom | UIRectEdgeLeft | UIRectEdgeRight;
    }
    self.title=@"MVVMDemo With TableView";
    self.view.backgroundColor=[UIColor whiteColor];
    
    tableView=[[UITableView alloc] initWithFrame:CGRectMake(0, 0, WScreen, HScreen-64) style:UITableViewStylePlain];
    [self.view addSubview:tableView];
    tableViewDataSource = [[TableViewDataSource alloc] init]; //对tableview 数据源的封装的类
    tableViewDelegate = [[TableViewDelegate alloc] init];     //对tableview 代理 封装的类,可以方便复用
    tableView.dataSource=tableViewDataSource;                 //把当前的tableview的数据源指向我们的公用datasource类。
    tableView.delegate=tableViewDelegate;                     //把当前的tableview的代理指向我们的公用的代理类。
    tableViewModel=[[TableViewModel alloc] init];             //本章的重头戏,把业务处理提出的类
    totalSource=0;
    
    //    YiRefreshHeader  头部刷新按钮的使用
    refreshHeader=[[YiRefreshHeader alloc] init];
    refreshHeader.scrollView=tableView;
    [refreshHeader header];
    __weak typeof(self) weakSelf = self;
    refreshHeader.beginRefreshingBlock=^(){
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf headerRefreshAction];                     //下拉刷新
    };
    
    //    是否在进入该界面的时候就开始进入刷新状态
    [refreshHeader beginRefreshing];
    
    //    YiRefreshFooter  底部刷新按钮的使用
    refreshFooter=[[YiRefreshFooter alloc] init];
    refreshFooter.scrollView=tableView;
    [refreshFooter footer];
    
    refreshFooter.beginRefreshingBlock=^(){
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf footerRefreshAction];                     //上拉刷新
    };
    
}

// 这里是具体的下拉刷新方法,我们可以发现,这里是调用了viewModel中的方法,并把处理的结果通过block方式返回回来。
- (void)headerRefreshAction
{
   
    [tableViewModel headerRefreshRequestWithCallback:^(NSArray *array){
        totalSource=(NSMutableArray *)array;
        tableViewDataSource.array=totalSource;
        tableViewDelegate.array=totalSource;
        [refreshHeader endRefreshing];
        [tableView reloadData];
    }];

}

// 这里是具体的上拉刷新方法,我们可以发现,这里是调用了viewModel中的方法,并把处理的结果通过block方式返回回来。
- (void)footerRefreshAction
{
    [tableViewModel footerRefreshRequestWithCallback:^(NSArray *array){
        [totalSource addObjectsFromArray:array] ;
        tableViewDataSource.array=totalSource;
        tableViewDelegate.array=totalSource;
        [refreshFooter endRefreshing];
        [tableView reloadData];
    
    }];
  
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end



TableViewModel.h

#import <Foundation/Foundation.h>

typedef void (^callback) (NSArray *array);

@interface TableViewModel : NSObject

//tableView头部刷新的网络请求
- (void)headerRefreshRequestWithCallback:(callback)callback;
//tableView底部刷新的网络请求
- (void)footerRefreshRequestWithCallback:(callback)callback;

@end

TableViewModel.m ,    这里应该非常的通俗易懂,就是把以前ViewController中的上下拉刷新方法放在此地,并且通过block把处理过后的结果返回给控制器。

#import "TableViewModel.h"
#import "CustomModel.h"
@interface TableViewModel ()

@end

@implementation TableViewModel

- (instancetype)init
{
    self = [super init];
    if (self) {
        
    }
    return self;
}

- (void)headerRefreshRequestWithCallback:(callback)callback
{
        //  后台执行:
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_async(dispatch_get_main_queue(), ^{
                //               主线程刷新视图
                NSMutableArray *arr=[NSMutableArray array];
                for (int i=0; i<16; i++) {
                    int x = arc4random() % 100;
                    NSString *string=[NSString stringWithFormat:@"下拉刷新-测试数据%d",x];
                    CustomModel *model=[[CustomModel alloc] init];
                    model.title=string;
                    [arr addObject:model];
                }
                callback(arr);
            });
        });
}

- (void )footerRefreshRequestWithCallback:(callback)callback
{
        //  后台执行:
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_async(dispatch_get_main_queue(), ^{
                //               主线程刷新视图
                NSMutableArray *arr=[NSMutableArray array];
                for (int i=0; i<16; i++) {
                    int x = arc4random() % 100;
                    NSString *string=[NSString stringWithFormat:@"上拉加载 -测试数据%d",x];
                    CustomModel *model=[[CustomModel alloc] init];
                    model.title=string;
                    [arr addObject:model];
                }
                callback(arr);
            });
        });
}

@end


TableViewDataSource.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface TableViewDataSource : NSObject<UITableViewDataSource>

@property (nonatomic,strong) NSArray *array;

@end


TableViewDataSource.m   把tableview的 数据源相关的代理方法全部拧出来,以后可以进行更高的复用

#import "TableViewDataSource.h"
#import "CustomTableViewCell.h"

@implementation TableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _array.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) {
        cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    cell.titleLabel.text=((CustomModel *)[_array objectAtIndex:indexPath.row]).title;
    return cell;
}

@end

TableViewDelegate.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface TableViewDelegate : NSObject<UITableViewDelegate>
@property (nonatomic,strong) NSArray *array;
@end

TableViewDelegate.m

这里我就把经常使用的tableview cell的点击代理给写了出来,大家可以随意进行添加其他的代理。 

#import "TableViewDelegate.h"

@implementation TableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (_array.count>0) {
        UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"" message:((CustomModel *)[_array objectAtIndex:indexPath.row]).title delegate:nil cancelButtonTitle:@"sure" otherButtonTitles:nil, nil];
        [alert show];
    }    
}
@end

其中。 model, 已经自定义的cell的具体代码我在这儿就不一一展示,和我们以前MVC的写法一模一样。  


项目代码下载地址: 点击打开链接


总结:

 1. 利用ViewModel把业务处理操作,全部从Controller中提出,这样在Controller中就不会充斥着大量的网络代理回调

2. 利用封装tableview的数据源 与 代理 类 ,来优化大量tableview的 协议 的冗余代码。减少代码量,方便后期维护与扩展。


后续:

      既然我们把业务操作提取出来了。那如何展示出来,怎么把对应的数据传递到view层等等,这时候就诞生了ReactiveCocoa, 虽然前期有自学过,但一直没有理解到其精髓,只是会使用一些基本的用法,运用到项目中间来,我怕会有一些意想不到的坑, 所以不敢拿公司的利益来冒险,以后有时间。 自己写写demo来运用下就好。 后期再出对应的博客详细讲解它


2016-05-12 16:48:33 mr_liu_easy_ios 阅读数 385
  • Microsoft Blend 2019/C#/.NET/MVVM/WCF/XAML/WPF/UWP

    本课程主要讲解Microsoft Blend For Visual Studio 2019,帮你一步步掌握Microsoft Blend的那令人激动的超强功能。如果你正在使用WPF、UWP技术进行开发,你一定需要学习Microsoft Blend,在这里你会发现,只需动动鼠标,你就可以迅速开发出又酷、又炫、又华丽应用程序界面,而系统自动为你生成全部的XAML代码。从此告别手工编写XAML的苦逼日子

    2705 人正在学习 去看看 赵庆明

在这呢也不赘述什么是MVC,神马又是MVVM了,在百度上谷歌一下一抓一大把,在这儿就简单的提上一嘴。下面的Demo用的就是MVVM的架构模式。

Model层是少不了的了,我们得有东西充当DTO(数据传输对象),当然,用字典也是可以的,编程么,要灵活一些。Model层是比较薄的一层,如果学过Java的小伙伴的话,对JavaBean应该不陌生吧。

ViewModel层,就是View和Model层的粘合剂,他是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。说白了,就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。

View层,就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示。

上面对MVVM就先简单的这么一说,好好的理解并应用的话,还得实战。



2017-07-05 22:26:00 weixin_30840253 阅读数 5
  • Microsoft Blend 2019/C#/.NET/MVVM/WCF/XAML/WPF/UWP

    本课程主要讲解Microsoft Blend For Visual Studio 2019,帮你一步步掌握Microsoft Blend的那令人激动的超强功能。如果你正在使用WPF、UWP技术进行开发,你一定需要学习Microsoft Blend,在这里你会发现,只需动动鼠标,你就可以迅速开发出又酷、又炫、又华丽应用程序界面,而系统自动为你生成全部的XAML代码。从此告别手工编写XAML的苦逼日子

    2705 人正在学习 去看看 赵庆明

好长一段时间没有敲简书了!
主要是因为一直在跑面试。
终于还是在上海入职了!
由于项目原因最终还是入了MVVM+RAC的坑

下面是正题。

Demo效果

使用MVVM+RAC请求网络数据
demo.gif

ReactiveCocoa简介

在iOS开发过程中,当某些事件响应的时候,需要处理某些业务逻辑,这些事件都用不同的方式来处理。
比如按钮的点击使用action,ScrollView滚动使用delegate,属性值改变使用KVO等系统提供的方式。
其实这些事件,都可以通过RAC处理
ReactiveCocoa为事件提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高聚合,低耦合的思想。

基础的话我还是推荐这篇博文 讲的都挺细的
当然不爽的话可以试试这个视频版的,也是某培训机构流出的

Demo分析

本文使用的是豆瓣API(非官方)
Demo所要做的功能很简单: 从网络中请求数据,并加载到UI上。
MVVM中最重要也就是这个VM了,VM通常与RAC紧密结合在一起,主要用于事务数据的处理和信号间的传递。

Demo中主要使用了下面这些第三方库

  pod 'SDWebImage'
  pod 'Motis'
  pod 'ReactiveCocoa', '2.5'
  pod 'BlocksKit'
  pod 'AFNetworking'
  pod 'Masonry'
  pod 'SVProgressHUD'

这里除了RAC 还有一个值得提一下

BlocksKit
众所周知Block已被广泛用于iOS编程。它们通常被用作可并发执行的逻辑单元的封装,或者作为事件触发的回调。Block比传统回调函数有2点优势: 

  • 允许在调用点上下文书写执行逻辑,不用分离函数
  • Block可以使用local variables.

    基于以上种种优点Cocoa Touch越发支持Block式编程,这点从UIView的各种动画效果可用Block实现就可以看出。而BlocksKit是对Cocoa Touch Block编程更进一步的支持,它简化了Block编程,发挥Block的相关优势,让更多UIKit类支持Block式编程。

代码

由于BlocksKit的使用,当我们写Delegate和Datasource时 就不用分离函数,整个逻辑都能凑在一起,比如这样定义一个collectionView:


- (void)initStyle {
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:[[UICollectionViewFlowLayout alloc] init]];
    collectionView.backgroundColor = [UIColor redColor];
    collectionView.showsHorizontalScrollIndicator = NO;
    collectionView.showsVerticalScrollIndicator = NO;
    collectionView.alwaysBounceVertical = YES;
    [self.view addSubview:collectionView];
    [collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.mas_topLayoutGuide);
        make.left.right.equalTo(self.view);
        make.bottom.equalTo(self.view.mas_bottom);
    }];
    self.collectionView = collectionView;
    
    //注册cell
    [self.collectionView registerClass:[MovieCollectionViewCell class] forCellWithReuseIdentifier:[MovieCollectionViewCell cellReuseIdentifier]];
    
    //collectionView dataSouce
    A2DynamicDelegate *dataSouce = self.collectionView.bk_dynamicDataSource;
    
    //item个数
    [dataSouce implementMethod:@selector(collectionView:numberOfItemsInSection:) withBlock:^NSInteger(UICollectionView *collectionView, NSInteger section) {
        return self.listArray.count;
    }];
    //item配置
    [dataSouce implementMethod:@selector(collectionView:cellForItemAtIndexPath:) withBlock:^UICollectionViewCell*(UICollectionView *collectionView,NSIndexPath *indexPath) {
        id<MovieModelProtocol> cell = nil;
        Class cellClass = [MovieCollectionViewCell class];
        if (cellClass) {
            cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MovieCollectionViewCell cellReuseIdentifier] forIndexPath:indexPath];
            if ([cell respondsToSelector:@selector(renderWithModel:)]) {
                [cell renderWithModel:self.listArray[indexPath.row]];
            }
        }
        return (UICollectionViewCell *)cell;
    }];
    self.collectionView.dataSource = (id)dataSouce;
    
#define scaledCellValue(value) ( floorf(CGRectGetWidth(collectionView.frame) / 375 * (value)) )

    //collectionView delegate
    A2DynamicDelegate *delegate = self.collectionView.bk_dynamicDelegate;
    
    //item Size
    [delegate implementMethod:@selector(collectionView:layout:sizeForItemAtIndexPath:) withBlock:^CGSize(UICollectionView *collectionView,UICollectionViewLayout *layout,NSIndexPath *indexPath) {
        return CGSizeMake(scaledCellValue(100), scaledCellValue(120));
    }];
    
    //内边距
    [delegate implementMethod:@selector(collectionView:layout:insetForSectionAtIndex:) withBlock:^UIEdgeInsets(UICollectionView *collectionView ,UICollectionViewLayout *layout, NSInteger section) {
        return UIEdgeInsetsMake(0, 15, 0, 15);
    }];

    self.collectionView.delegate = (id)delegate;
    
}

这就将所有有关collectionView的内容都包含在一起了,这样更符合逻辑。

我们让viewModel来处理网络请求,controller需要做的就是启动这个开关,并接受数据而已,所有的工作交给viewModel来处理

MovieViewModel.m

- (void)initViewModel {
    @weakify(self);
    self.command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        @strongify(self);
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            @strongify(self);
            [self getDoubanList:^(NSArray<MovieModel *> *array) {
                [subscriber sendNext:array];
                [subscriber sendCompleted];
            }];
            return nil;
        }];
    }];

}



/**
 网络请求

 @param succeedBlock 成功回调
 */
- (void)getDoubanList:(void(^)(NSArray<MovieModel*> *array))succeedBlock {
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    [manager GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSMutableArray *array = [NSMutableArray array];
        MoviewModelList *base = [[MoviewModelList alloc] init];
        [base mts_setValuesForKeysWithDictionary:responseObject];
        
        //遍历数组取出 存入数组并回调出去
        [base.subjects enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            MovieModel *model = [[MovieModel alloc] init];
            [model mts_setValuesForKeysWithDictionary:obj];
            [array addObject:model];
        }];
        if (succeedBlock) {
            succeedBlock(array);
        }
    } failure:nil];
    
}

ViewController.m

- (void)bindViewModel {
    @weakify(self);
    //将命令执行后的数据交给controller
    [self.viewModel.command.executionSignals.switchToLatest subscribeNext:^(NSArray<MovieModel *> *array) {
        @strongify(self);
        [SVProgressHUD showSuccessWithStatus:@"加载成功"];
        self.listArray = array;
        [self.collectionView reloadData];
        [SVProgressHUD dismissWithDelay:1.5];
    }];
    
    //执行command
    [self.viewModel.command execute:nil];
    [SVProgressHUD showWithStatus:@"加载中..."];
}

Demo地址

GitHub

转载于:https://www.cnblogs.com/gongxiaokai/p/7123837.html

2018-09-29 17:35:42 u012881779 阅读数 1497
  • Microsoft Blend 2019/C#/.NET/MVVM/WCF/XAML/WPF/UWP

    本课程主要讲解Microsoft Blend For Visual Studio 2019,帮你一步步掌握Microsoft Blend的那令人激动的超强功能。如果你正在使用WPF、UWP技术进行开发,你一定需要学习Microsoft Blend,在这里你会发现,只需动动鼠标,你就可以迅速开发出又酷、又炫、又华丽应用程序界面,而系统自动为你生成全部的XAML代码。从此告别手工编写XAML的苦逼日子

    2705 人正在学习 去看看 赵庆明

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

示意图:

 

2016-07-06 10:11:33 qq_16578397 阅读数 9161
  • Microsoft Blend 2019/C#/.NET/MVVM/WCF/XAML/WPF/UWP

    本课程主要讲解Microsoft Blend For Visual Studio 2019,帮你一步步掌握Microsoft Blend的那令人激动的超强功能。如果你正在使用WPF、UWP技术进行开发,你一定需要学习Microsoft Blend,在这里你会发现,只需动动鼠标,你就可以迅速开发出又酷、又炫、又华丽应用程序界面,而系统自动为你生成全部的XAML代码。从此告别手工编写XAML的苦逼日子

    2705 人正在学习 去看看 赵庆明

iOS MVVM模式(Model-View-ViewModel):
1. Model层是少不了的了,我们得有东西充当DTO(数据传输对象),当然,用字典也是可以的,编程么,要灵活一些。Model层是比较薄的一层,如果学过Java的小伙伴的话,对JavaBean应该不陌生吧。
2. ViewModel层,就是View和Model层的粘合剂,他是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。说白了,就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。
3. View层,就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示。项目MVVM的使用


MVVM实现的基本条件(限本博客):
- 1. JSONModel
- 2. ReactiveCocoa(响应式编程)
- 3. AFNetworking
- 4. AFNetworking-RACExtensions–>AFNetworking-RACExtensions
- 5. JSONModel-RACExtension–>JSONModel-RACExtensions
- 6. 此外需要在自己的mac上搭建虚拟服务器模拟网络请求
- 7. CocoaPods的安装使用

废话不多说,看部分代码(代码较多)!
基本结构

Model文件夹


#TestModel.h文件
#import "XCBaseModel.h"
#import "userinfoModel.h"

@interface TestModel : XCBaseModel

@property NSString* city;
@property userinfoModel *user_info;

@end


#import <JSONModel/JSONModel.h>

@interface userinfoModel : JSONModel

@property NSString* user_name;
@property NSNumber* user_code;

@end

NetWork文件夹

XCAPIManager+test.m

#import "XCAPIManager+test.h"
#import "XCAPIManager+Analysis.h"
#import "TestModel.h"

@implementation XCAPIManager (test)

- (RACSignal *)test
{
    //此处使用的mac自带的模拟服务器
    return [[[[[self rac_GET:@"http://localhost/~liuxiaochun/a.json"(http://yangjunwei.com/a/1568.html) parameters:@{}] map:^id(id value) {
        return [self analysisRequest:value];
    }] flattenMap:^RACStream *(id value) {
        if([value isKindOfClass:[NSError class]]){
            return [RACSignal return:value];
        }
        return [self rac_MappingForClass:[TestModel class] array:value[@"topic_infos"]];
    }] logError] replayLazily];
}

@end

View/ViewController文件夹

ViewController.m

#import "ViewController.h"
#import "TestViewModel.h"
#import "XCTableviewCellTest.h"

@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-property-synthesis"
@property (strong, nonatomic) IBOutlet TestViewModel *viewModel;
#pragma clang diagnostic pop

@property (nonatomic,strong)NSArray* models;
@property (weak, nonatomic) IBOutlet UITableView *tableview;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    @weakify(self)
    [self.viewModel.testCommand execute:nil];
    [RACObserve(self.viewModel, tests) subscribeNext:^(NSArray* x) {
        @strongify(self)
        self.models = x;
        [self.tableview reloadData];
    }];
    // Do any additional setup after loading the view, typically from a nib.
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - delegate / datasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.models.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString* cellId = @"cell";
    XCTableviewCellTest* cell = [tableView dequeueReusableCellWithIdentifier:cellId];

    [cell bindModel:self.models[indexPath.row]];
    return cell;
}
-----------------
XCTableviewCellTest.m

#import "XCTableviewCellTest.h"
@implementation XCTableviewCellTest
- (void)bindModel:(TestModel *)model
{
    self.data1.text = model.city;
    self.data2.text = model.user_info.user_name;
    self.data3.text = [NSString stringWithFormat:@"%@",model.user_info.user_code];
}
@end

ViewModel文件夹

#import "TestViewModel.h"
#import "XCAPIManager+test.h"

@interface TestViewModel ()

@property (nonatomic,strong,readwrite) RACCommand* testCommand;
@property (nonatomic,strong,readwrite) NSArray* tests;

@end

@implementation TestViewModel

- (instancetype)init {
    if (self = [super init]) {

    }
    return self;
}

- (RACCommand *)testCommand
{
    if (!_testCommand) {
        @weakify(self)
        _testCommand = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) {
            @strongify(self)
            return [self.apiManager test];
        }];

        [[_testCommand.executionSignals concat] subscribeNext:^(NSArray* x) {
            @strongify(self)
            self.tests = x;
            NSLog(@"%@",x);
        }];
//        XPViewModelShortHand(_testCommand)
    }
    return _testCommand;
}
@end

demo: –> XCMVVM

没有更多推荐了,返回首页