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.
有问题欢迎探讨.