OC工作笔记

OC.地址选择器

2018-09-17  本文已影响202人  王四猫
效果图

前言:

原本为Swift版本Swift.地址选择器,因为有朋友需要一个OC版本,所以将其改写.但是由于有段时间没用OC了,所以可能会出现优化问题,这里我还是建议参考Swift版本.另如果发现任何问题请向我反馈,我好及时修改,谢谢.

实现效果:

controller弹出时:半透明背景渐变展示.地址选择器从下方弹出.

地址选择器:以省份,城市,地区三级进行选择,数据来自本地plist文件.有12个热门城市供快速选择,选择错误可以回选.

选择地区时进行将数据回调到上一控制器,点击页面空白区域退出controller.

controller消失时:背景渐变消失,地址选择器向下退出.

实现思路:

本质上来说这是一个复杂版的日期选择器,有关弹出动画完全可以拿过来就用.

所以主要复杂的点就是这个自定制的地址选择器.

具体实现是写一个view其中包含tableView.给view三种type.来决定它当前的状态.然后根据type的改变来修改他的样式和数据展示,以及cell被点击时执行方法.

实现方式:

1.找到数据源,声明model将数据转成对象.

2.实现地址选择器的样式,数据展示.

3.实现我们需要的转场视图动画.

4.将地址选择器加入一个ViewController,并修改其转场动画代理,使其使用自定制转场动画.

5.实现地址选择器的功能,以及交互优化.


1.找到数据源,声明model将数据转成对象.

数据来自网络,一个plist文件.将其转换成model进行保存.

//
//  EWAddressModel.m
//  EWAddressPicker-OC
//
//  Created by Ethan.Wang on 2018/9/14.
//  Copyright © 2018年 Ethan. All rights reserved.
//

#import "EWAddressModel.h"
///总数据
@implementation EWCountryModel 
- (instancetype)initWithDic:(NSDictionary *)dic{
    self = [super init];
    if (self){
        NSArray *keyArray = dic.allKeys;
        NSArray *valueArray = dic.allValues;
        _countryDictionary = [NSMutableDictionary dictionary];
        _provincesArray = keyArray;
        for (int i = 0; i < keyArray.count; i++) {
            EWProvinceModel *provinceModel = [[EWProvinceModel alloc] initWithDic:valueArray[i]];
            [_countryDictionary setValue:provinceModel forKey:keyArray[i]];
        }
    }
    return self;
}
@end
///省份数据
@implementation EWProvinceModel
- (instancetype)initWithDic:(NSDictionary *)dic{
    self = [super init];
    if (self){
        NSArray *keyArray = dic.allKeys;
        NSArray *valueArray = dic.allValues;
        _provincesDictionary = [NSMutableDictionary dictionary];
        _cityArray = keyArray;
        for (int i = 0; i < keyArray.count; i++) {
            EWCityModel *cityModel = [[EWCityModel alloc] initWithArr:valueArray[i]];
            [_provincesDictionary setValue:cityModel forKey:keyArray[i]];
        }
    }
    return self;
}
@end

///城市数据
@implementation EWCityModel
- (instancetype)initWithArr:(NSArray *)arr{
    self = [super init];
    if (self){
        self.areaArray = arr;
    }
    return self;
}
@end


/**
 从文件中获取城市数据
 */
- (void)initLocationData{
    NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"area" ofType:@"plist"]];
    self.locationModel = [[EWCountryModel alloc]initWithDic:dic];
    self.dataArray = _locationModel.provincesArray;
}

2.实现地址选择器的样式,数据展示.

  - (void)buildTitleScrollView{
    ///每次切换状态显示时重新init,保证动画正常显示
    if (titleSV != nil){
        [titleSV removeFromSuperview];
    }
    _buttonArray = [NSMutableArray array];
    titleSV = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 72, [UIScreen mainScreen].bounds.size.width, 44)];
    underLine = [[UIView alloc]initWithFrame:CGRectMake(0, 40, 30, 2)];
    underLine.backgroundColor = _selectColor;
    for (int i = 0; i < 3; i++){
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = CGRectMake(24 + i * ([UIScreen mainScreen].bounds.size.width - 47) / 3, 0, [UIScreen mainScreen].bounds.size.width / 3, 44);
        button.tag = i;
        if (i == 1){
            button.selected = YES;
            underLine.center = CGPointMake(button.center.x, 40);
        }
        ///为Button设定normal状态和selected状态
        [button setTitle:@"请选择" forState:UIControlStateNormal];
        [button setTitleColor:[UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1] forState:UIControlStateNormal];
        [button setTitleColor:_selectColor forState:UIControlStateSelected];
        button.titleLabel.font = [UIFont systemFontOfSize:12];
        button.titleLabel.adjustsFontSizeToFitWidth = YES;
        [button addTarget:self action:@selector(onClickTitleButton:) forControlEvents: UIControlEventTouchUpInside];
        [_buttonArray addObject:button];
        [titleSV addSubview:button];
        titleSV.showsVerticalScrollIndicator = NO;
        [titleSV addSubview:underLine];
        titleSV.contentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 44);
        titleSV.hidden = YES;
        [self addSubview:titleSV];
    }
}
- (void)drawTableView{
    tableViewHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 160)];
    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(24, 0, 50, 18)];
    label.textColor = [UIColor colorWithRed:102/255.0 green:102/255.0 blue:102/255.0 alpha:1];
    label.font = [UIFont systemFontOfSize:12];
    label.text = @"热门城市";
    [tableViewHeaderView addSubview:label];
    for (int i = 0; i < 12; i++){
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = CGRectMake(24 + 80 * (i % 4), 28 + 40 * (i / 4), 80, 40);
        [button setTitle:_hotCiryArray[i] forState:UIControlStateNormal];
        [button setTitleColor:[UIColor colorWithRed:102/255.0 green:102/255.0 blue:102/255.0 alpha:1] forState:UIControlStateNormal];
        button.titleLabel.font = [UIFont systemFontOfSize:12];
        [button addTarget:self action:@selector(onClickHotCity:) forControlEvents:UIControlEventTouchUpInside];
        button.tag = i;
        [tableViewHeaderView addSubview:button];
    }

    tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 42, [UIScreen mainScreen].bounds.size.width, 458) style:UITableViewStylePlain];
    tableView.delegate = self;
    tableView.dataSource = self;
    tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    tableView.tableHeaderView = tableViewHeaderView;
    [self addSubview:tableView];

}

3.实现我们需要的转场视图动画.

   /**
 动画时间
 */
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
    return 0.3;
}

- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
    if (_type == present){
        EWAddressViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        UIView *toView = toVC.view;
        UIView *containerView = transitionContext.containerView;
        [containerView addSubview:toView];
        toVC.containV.transform = CGAffineTransformMakeTranslation(0, toVC.containV.frame.size.height);

        [UIView animateWithDuration:0.25 animations:^{
            /// 背景变色
            toVC.backgroundView.alpha = 1.0;
            /// addresspicker向上推出
            toVC.containV.transform = CGAffineTransformMakeTranslation(0, -10);
        } completion:^(BOOL finished) {
            [UIView animateWithDuration:0.2 animations:^{
                /// transform初始化
                toVC.containV.transform = CGAffineTransformIdentity;
            } completion:^(BOOL finished) {
                [transitionContext completeTransition:true];
            }];
        }];
    }else {
        EWAddressViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        [UIView animateWithDuration:0.25 animations:^{
            toVC.backgroundView.alpha = 0.0;
            /// addresspicker向下推回
            toVC.containV.transform = CGAffineTransformMakeTranslation(0, toVC.containV.frame.size.height);
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:true];
        }];
    }
}

4.将地址选择器加入一个ViewController,并修改其转场动画代理,使其使用自定制转场动画.

- (void)drawMyView{
    [self.view insertSubview:self.backgroundView atIndex:0];
    self.providesPresentationContextTransitionStyle = YES;
    self.definesPresentationContext = YES;
    //viewcontroller弹出后之前控制器页面不隐藏 .custom代表自定义
    self.modalTransitionStyle = UIModalPresentationCustom;

    _containV = [[EWAddressPickerView alloc]initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height - 550, [UIScreen mainScreen].bounds.size.width, 550) selectColor:[UIColor colorWithRed:79/255.0 green:176/255.0 blue:255.0/255.0 alpha:1]];
    ///弱引用,防止循环引用
    __weak typeof(self) weakSelf = self;
    _containV.backOnClickCancel = ^{
        [weakSelf onClickCancel];
    };
    _containV.backLocationString = ^(NSString *address, NSString *province, NSString *city, NSString *area) {
        ///闭包回调
        weakSelf.backLocationString(address, province, city, area);
        [weakSelf onClickCancel];
    };
    [self.view addSubview:_containV];
    // 转场动画代理
    self.transitioningDelegate = self;
}

///推入动画
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
    EWAddressPickerPresentAnimated *animated = [[EWAddressPickerPresentAnimated alloc] initWithType:present];
    return animated;
}
///推出动画
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
    EWAddressPickerPresentAnimated *animated = [[EWAddressPickerPresentAnimated alloc] initWithType:dismiss];
    return animated;
}

5.实现地址选择器的功能,以及交互优化.

主要复杂的部分,只能展示其中一部分代码,具体实现还是要在demo中看.

///type的set方法
-(void)setType:(enum EWLocationPickViewTableViewType)type{
    _type = type;
    switch (type) {
        case provinces:
            ///省份模式下有热门城市headerView,没有titleScrollView
            tableView.tableHeaderView = tableViewHeaderView;
            tableView.frame = CGRectMake(0, 42, [UIScreen mainScreen].bounds.size.width, 458);
            titleSV.hidden = YES;
            leftLabel.hidden = YES;
            ///清空选择数据
            self.provincesModel = nil;
            self.selectedProvince = @"";
            self.selectedCity = @"";
            self.selectedArea = @"";
            self.cityModel = nil;
            ///修改titleScrollView中button的样式,已保证选择省份后有下划线滚动的动画效果
            for (UIButton *button in _buttonArray) {
                [button setTitle:@"请选择" forState: UIControlStateNormal];
                button.selected = NO;
                if (button.tag == 0){
                    button.selected = YES;
                }
            }
            underLine.center = CGPointMake(self.buttonArray[1].center.x, underLine.center.y);
            ///tableView加载省份数据
            self.dataArray = _locationModel.provincesArray;
            [tableView reloadData];
            break;
        case city:
        {
            ///城市模式下没有热门城市headerView,有titleScrollView
            tableView.tableHeaderView = [[UIView alloc]init];
            tableView.frame = CGRectMake(0, 136, [UIScreen mainScreen].bounds.size.width, 367);
            titleSV.hidden = NO;
            leftLabel.hidden = NO;
            ///保留选择省份,清空城市地区数据
            self.selectedCity = @"";
            self.selectedArea = @"";
            self.cityModel = nil;
            for (UIButton *button in _buttonArray) {
                button.selected = NO;
                if (button.tag != 0) {
                    [button setTitle:@"请选择" forState:UIControlStateNormal];
                }
                if (button.tag == 1) {
                    button.selected = YES;
                }
            }
            [UIView animateWithDuration:0.3 animations:^{
                underLine.center = CGPointMake(self.buttonArray[1].center.x, underLine.center.y);
            }];
            ///tableView加载城市数据
            self.dataArray = _provincesModel.cityArray;
            [tableView reloadData];
            break;
        }
        case area:
            tableView.tableHeaderView = [[UIView alloc]init];
            tableView.frame = CGRectMake(0, 136, [UIScreen mainScreen].bounds.size.width, 367);
            titleSV.hidden = NO;
            leftLabel.hidden = NO;
            for (UIButton *button in _buttonArray) {
                button.selected = NO;
                if (button.tag == 2){
                    button.selected = YES;
                }
            }
            [UIView animateWithDuration:0.3 animations:^{
                underLine.center = CGPointMake(self.buttonArray[2].center.x, underLine.center.y);
            }];
            self.dataArray = _cityModel.areaArray;
            [tableView reloadData];
            break;
    }
}

使用方式:

将demo中EWAddressPicker文件夹拖入项目,在需要的ViewController中实现

 EWAddressViewController *VC = [[EWAddressViewController alloc]init];
    self.definesPresentationContext = YES;
    VC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
  ///block弱引用防止循环引用.
    __weak typeof(self) weakSelf = self;
    VC.backLocationString = ^(NSString *address, NSString *province, NSString *city, NSString *area) {
         // 返回选择数据,地址,省,市,区
        weakSelf.showLabel.text = address;
    };
    [self presentViewController:VC animated:true completion:nil];

demo地址:EWAddressPicker-OC.求Star.

有问题欢迎探讨.

上一篇下一篇

猜你喜欢

热点阅读