Files
ln-ios/ln_jq_app/ios/AMapNavIOSSDK/AMapNavIOSSDK/Classes/Class/AStationDetailPopupController.m

486 lines
18 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// AStationDetailPopupController.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/22.
//
#import "AStationDetailPopupController.h"
#import "ABaseViewController.h"
#import "AMapNavCommonUtil.h"
#import <Masonry/Masonry.h>
// 主题绿色(开始导航按钮背景)
static inline UIColor *AStationThemeGreen(void) {
return [UIColor colorWithRed:0x1A/255.0 green:0x7C/255.0 blue:0x43/255.0 alpha:1.0];
}
@interface AStationDetailPopupController ()
/// 背景蒙层
@property (nonatomic, strong) UIControl *maskControl;
/// 弹框卡片容器
@property (nonatomic, strong) UIView *cardView;
/// 站点名称
@property (nonatomic, strong) UILabel *stationNameLabel;
/// 预计加氢费用(名称右侧)
@property (nonatomic, strong) UIImageView *costIconView;
@property (nonatomic, strong) UILabel *costLabel;
/// 地址
@property (nonatomic, strong) UILabel *addressLabel;
/// 分割线
@property (nonatomic, strong) UIView *separator;
/// 预计时间行
@property (nonatomic, strong) UIImageView *timeIconView;
@property (nonatomic, strong) UILabel *timeLabel;
/// 行驶里程
@property (nonatomic, strong) UIImageView *distanceIconView;
@property (nonatomic, strong) UILabel *distanceLabel;
/// 过路费
@property (nonatomic, strong) UIImageView *tollIconView;
@property (nonatomic, strong) UILabel *tollLabel;
/// 关闭按钮
@property (nonatomic, strong) UIButton *closeButton;
/// 开始导航按钮
@property (nonatomic, strong) UIButton *startNaviButton;
/// 卡片 bottom constraint动画用
@property (nonatomic, strong) MASConstraint *cardBottomConstraint;
@end
@implementation AStationDetailPopupController
#pragma mark - Life Cycle
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor];
[self _buildUI];
[self _setupMasonryConstraints];
[self _updateUI];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self _playShowAnimation];
}
#pragma mark - Public
- (void)presentInViewController:(UIViewController *)parentVC {
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[parentVC presentViewController:self animated:NO completion:nil];
}
- (void)dismiss {
[self dismissWithCompletion:nil];
}
- (void)dismissWithCompletion:(void(^)(void))completion {
[self _playDismissAnimationWithCompletion:^{
[self dismissViewControllerAnimated:NO completion:completion];
}];
}
#pragma mark - Setter Override弹出前赋值 或 弹出后动态更新)
- (void)setPointModel:(ANavPointModel *)pointModel {
_pointModel = pointModel;
if (self.isViewLoaded) [self _updateUI];
}
- (void)setEstimatedCost:(NSString *)estimatedCost {
_estimatedCost = [estimatedCost copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setEstimatedTime:(NSString *)estimatedTime {
_estimatedTime = [estimatedTime copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setDriveDistance:(NSString *)driveDistance {
_driveDistance = [driveDistance copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setTollFee:(NSString *)tollFee {
_tollFee = [tollFee copy];
if (self.isViewLoaded) [self _updateUI];
}
#pragma mark - Build UI
/**
卡片结构:
┌──────────────────────────────────── [✕] ┐
│ 站点名称(加粗) 预计加氢费用:--元 │ ← 同行间距20pt
│ 地址(灰色小字,多行) │
│ ──────────────────────────────────────── │
│ [icon] 预计时间:-- 分钟 │
│ [icon] 行驶里程:-- 公里 [icon] 过路费:-- 元 │
│ ╔════════════════════════════════════╗ │
│ ║ 开始导航 ║ │
│ ╚════════════════════════════════════╝ │
│ (距底部30pt) │
└─────────────────────────────────────────┘
*/
- (void)_buildUI {
// ── 蒙层 ──
UIControl *mask = [[UIControl alloc] init];
mask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.35];
[mask addTarget:self action:@selector(_onMaskTapped) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:mask];
self.maskControl = mask;
// ── 卡片 ──
UIView *card = [[UIView alloc] init];
card.backgroundColor = [UIColor whiteColor];
card.layer.cornerRadius = 16;
card.layer.masksToBounds = NO;
card.layer.shadowColor = [UIColor blackColor].CGColor;
card.layer.shadowOpacity = 0.15;
card.layer.shadowRadius = 12;
card.layer.shadowOffset = CGSizeMake(0, -4);
[self.view addSubview:card];
self.cardView = card;
// ── 关闭按钮 ──
UIButton *closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
// [closeBtn setTitle:@"✕" forState:UIControlStateNormal];
[closeBtn setImage: [AMapNavCommonUtil imageWithName:@"icon_close"] forState:UIControlStateNormal];
[closeBtn setTitleColor:[UIColor colorWithWhite:0.5 alpha:1] forState:UIControlStateNormal];
closeBtn.titleLabel.font = [UIFont systemFontOfSize:16];
[closeBtn addTarget:self action:@selector(_onCloseTapped) forControlEvents:UIControlEventTouchUpInside];
[card addSubview:closeBtn];
self.closeButton = closeBtn;
// ── 站点名称 ──
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.font = [UIFont boldSystemFontOfSize:18];
nameLabel.textColor = [UIColor colorWithWhite:0.1 alpha:1];
nameLabel.numberOfLines = 2;
// nameLabel.adjustsFontSizeToFitWidth = YES;
nameLabel.minimumScaleFactor = 0.8;
[card addSubview:nameLabel];
self.stationNameLabel = nameLabel;
// ── 预计加氢费用名称右侧间距20pt ──
UILabel *costLabel = [[UILabel alloc] init];
costLabel.font = [UIFont systemFontOfSize:14];
costLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1];
costLabel.numberOfLines = 1;
costLabel.textAlignment = NSTextAlignmentLeft;
// [costLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
// [costLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[card addSubview:costLabel];
self.costLabel = costLabel;
// ── 地址 ──
UILabel *addrLabel = [[UILabel alloc] init];
addrLabel.font = [UIFont systemFontOfSize:13];
addrLabel.textColor = [UIColor colorWithWhite:0.5 alpha:1];
addrLabel.numberOfLines = 2;
[card addSubview:addrLabel];
self.addressLabel = addrLabel;
UIImageView *costIcon = [[UIImageView alloc] init];
costIcon.contentMode = UIViewContentModeScaleAspectFit;
costIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_fuel"];
[card addSubview:costIcon];
self.costIconView = costIcon;
// ── 分割线 ──
UIView *sep = [[UIView alloc] init];
sep.backgroundColor = [UIColor colorWithWhite:0.88 alpha:1];
[card addSubview:sep];
self.separator = sep;
// ── 预计时间图标 ──
UIImageView *timeIcon = [[UIImageView alloc] init];
timeIcon.contentMode = UIViewContentModeScaleAspectFit;
timeIcon.image = [AMapNavCommonUtil imageWithName3x:@"pre_time_icon"];
[card addSubview:timeIcon];
self.timeIconView = timeIcon;
// ── 预计时间文字 ──
UILabel *timeLabel = [[UILabel alloc] init];
timeLabel.font = [UIFont systemFontOfSize:14];
timeLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1];
[card addSubview:timeLabel];
self.timeLabel = timeLabel;
// ── 行驶里程图标 ──
UIImageView *distIcon = [[UIImageView alloc] init];
distIcon.contentMode = UIViewContentModeScaleAspectFit;
distIcon.image = [AMapNavCommonUtil imageWithName3x:@"pre_distance_icon"];
[card addSubview:distIcon];
self.distanceIconView = distIcon;
// ── 行驶里程文字 ──
UILabel *distLabel = [[UILabel alloc] init];
distLabel.font = [UIFont systemFontOfSize:14];
distLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1];
[card addSubview:distLabel];
self.distanceLabel = distLabel;
// ── 过路费图标 ──
UIImageView *tollIcon = [[UIImageView alloc] init];
tollIcon.contentMode = UIViewContentModeScaleAspectFit;
tollIcon.image = [AMapNavCommonUtil imageWithName3x:@"pre_cost_icon"];
[card addSubview:tollIcon];
self.tollIconView = tollIcon;
// ── 过路费文字 ──
UILabel *tollLabel = [[UILabel alloc] init];
tollLabel.font = [UIFont systemFontOfSize:14];
tollLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1];
[card addSubview:tollLabel];
self.tollLabel = tollLabel;
// ── 开始导航按钮 ──
UIButton *naviBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[naviBtn setTitle:@"开始导航" forState:UIControlStateNormal];
[naviBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
naviBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
naviBtn.backgroundColor = AStationThemeGreen();
naviBtn.layer.cornerRadius = 24;
[naviBtn addTarget:self action:@selector(_onStartNaviTapped) forControlEvents:UIControlEventTouchUpInside];
[card addSubview:naviBtn];
self.startNaviButton = naviBtn;
}
#pragma mark - Masonry Constraints
- (void)_setupMasonryConstraints {
UIView *card = self.cardView;
CGFloat iconSize = 16;
// ── 蒙层:铺满父视图 ──
[self.maskControl mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
// ── 卡片左右各16底部距 safeArea 30pt ──
// 初始状态卡片在屏幕下方动画起点top 留空,先用 bottom 约束做弹入
[card mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.right.equalTo(self.view).offset(-16);
// 动画起点:卡片顶部 = 父视图底部(完全隐藏在屏幕外)
make.top.equalTo(self.view.mas_bottom).offset(0);
}];
// ── 关闭按钮:右上角 ──
[self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(card).offset(8);
make.right.equalTo(card).offset(-15);
make.width.height.mas_equalTo(40);
}];
// ── 第一行:站点名称(左,内容自适应)+ 预计加氢费用紧跟nameLabel右侧20pt ──
// nameLabel内容有多宽占多宽宽度上限55%,防止过长把费用挤掉
[self.stationNameLabel setContentHuggingPriority:UILayoutPriorityDefaultLow
forAxis:UILayoutConstraintAxisHorizontal];
[self.stationNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(card).offset(25);
make.left.equalTo(card).offset(16);
// make.width.lessThanOrEqualTo(card).multipliedBy(0.45);
make.right.equalTo(self.closeButton.mas_left).offset(-12);
}];
// ── 地址名称下方6pt间距 ──
[self.addressLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.stationNameLabel.mas_bottom).offset(10);
make.left.equalTo(card).offset(16);
make.right.equalTo(card).offset(-16);
}];
// ── 分割线地址下方12pt间距 ──
[self.separator mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.addressLabel.mas_bottom).offset(15);
make.left.equalTo(card).offset(16);
make.right.equalTo(card).offset(-16);
make.height.mas_equalTo(0.5);
}];
// ── 预计时间行分割线下方14pt ──
[self.timeIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.separator.mas_bottom).offset(15);
make.left.equalTo(card).offset(16);
make.width.height.mas_equalTo(iconSize);
}];
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.timeIconView);
make.left.equalTo(self.timeIconView.mas_right).offset(6);
// make.right.equalTo(card).offset(-16);
make.height.mas_equalTo(24);
}];
///cost
[self.costIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.timeIconView);
make.left.equalTo(self.timeLabel.mas_right).offset(30);
make.width.height.mas_equalTo(iconSize);
}];
[self.costLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.costIconView);
make.left.equalTo(self.costIconView.mas_right).offset(6);
make.right.lessThanOrEqualTo(card).offset(-10);
}];
// ── 行驶里程 + 过路费行时间行下方12pt ──
[self.distanceIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.timeIconView.mas_bottom).offset(15);
make.left.equalTo(card).offset(16);
make.width.height.mas_equalTo(iconSize);
}];
[self.distanceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.distanceIconView);
make.left.equalTo(self.distanceIconView.mas_right).offset(6);
// 宽度约为可用区域的一半(另一半留给过路费)
make.width.mas_lessThanOrEqualTo(130);
make.height.mas_equalTo(24);
}];
[self.tollIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.distanceIconView);
make.left.equalTo(self.costIconView.mas_left);
make.width.height.mas_equalTo(iconSize);
}];
[self.tollLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.distanceIconView);
make.left.equalTo(self.tollIconView.mas_right).offset(6);
// make.right.equalTo(card).offset(-16);
make.height.mas_equalTo(24);
}];
// ── 开始导航按钮里程行下方18pt距卡片底部30pt ──
[self.startNaviButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.distanceIconView.mas_bottom).offset(50);
make.left.equalTo(card).offset(16);
make.right.equalTo(card).offset(-16);
make.height.mas_equalTo(48);
make.bottom.equalTo(card).offset(-AMP_TabbarSafeBottomMargin); // 卡片底部内间距30pt
}];
}
#pragma mark - Data Update
- (void)_updateUI {
self.stationNameLabel.text = (self.pointModel.name.length > 0)
? self.pointModel.name : @"--";
self.costLabel.text = (self.estimatedCost.length > 0)
? [NSString stringWithFormat:@"预计加氢费用:%@元", self.estimatedCost]
: @"预计加氢费用:--元";
self.addressLabel.text = (self.pointModel.address.length > 0)
? self.pointModel.address : @"--";
// ── 预计时间(始终显示,无值显示"-- 分钟" ──
self.timeLabel.text = (self.estimatedTime.length > 0)
? [NSString stringWithFormat:@"预计时间:%@分钟", self.estimatedTime]
: @"预计时间:--分钟";
// ── 行驶里程(始终显示,无值显示"-- 公里" ──
self.distanceLabel.text = (self.driveDistance.length > 0)
? [NSString stringWithFormat:@"行驶里程:%@公里", self.driveDistance]
: @"行驶里程:--公里";
// ── 过路费(始终显示,无值显示"-- 元" ──
self.tollLabel.text = (self.tollFee.length > 0)
? [NSString stringWithFormat:@"过路费:%@元", self.tollFee]
: @"过路费:--元";
}
#pragma mark - Animation
/**
弹入动画:更新 top 约束将卡片滑入至距底部 30pt含 safeArea
*/
- (void)_playShowAnimation {
// 目标状态:卡片底部紧贴 safeArea 底部,再往上留 30pt
[self.cardView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(0);
make.right.equalTo(self.view).offset(-0);
make.bottom.equalTo(self.view).offset(0);
}];
self.maskControl.alpha = 0;
[UIView animateWithDuration:0.36
delay:0
usingSpringWithDamping:0.82
initialSpringVelocity:0.5
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.maskControl.alpha = 1;
[self.view layoutIfNeeded];
} completion:nil];
}
- (void)_playDismissAnimationWithCompletion:(void(^)(void))completion {
[self.cardView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.right.equalTo(self.view).offset(-16);
make.top.equalTo(self.view.mas_bottom).offset(20);
}];
[UIView animateWithDuration:0.25
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.maskControl.alpha = 0;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
if (completion) completion();
}];
}
#pragma mark - Actions
- (void)_onMaskTapped {
if ([self.delegate respondsToSelector:@selector(stationDetailPopupDidTapClose:)]) {
[self.delegate stationDetailPopupDidTapClose:self];
}
[self dismiss];
}
- (void)_onCloseTapped {
if ([self.delegate respondsToSelector:@selector(stationDetailPopupDidTapClose:)]) {
[self.delegate stationDetailPopupDidTapClose:self];
}
[self dismiss];
}
- (void)_onStartNaviTapped {
// 先关闭弹框(等动画完全结束),再通知 delegate 弹出导航页
// 避免两个 presentViewController 同时进行导致导航页面无法显示
__weak typeof(self) weakSelf = self;
[self dismissWithCompletion:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if ([strongSelf.delegate respondsToSelector:@selector(stationDetailPopupDidTapStartNavi:)]) {
[strongSelf.delegate stationDetailPopupDidTapStartNavi:strongSelf];
}
}];
}
@end