OC.地址選擇器
阿新 • • 發佈:2018-11-09
#前言:
原本為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.
有問題歡迎探討.