jsdw_ios/QuickLocation/Section/Home/SOS/SOSListViewController.swift

355 lines
14 KiB
Swift
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.

import UIKit
import ObjectMapper
import RxSwift
struct SOSListResponse: BaseModelProtocol {
var code: String?
var message: String?
var list: [SOSListItem] = []
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- (map["code"], kIntTransformStr)
message <- map["message"]
if message == nil {
message <- map["msg"]
}
list <- map["data"]
if list.isEmpty {
list <- map["data.list"]
}
}
}
struct SOSListItem: Mappable {
var userId = ""
var nickName = ""
var headPic = ""
var lastPosition = ""
var groupKey = ""
var groupName = ""
var sosTime = ""
init?(map: Map) {}
mutating func mapping(map: Map) {
userId <- (map["user_id"], kIntTransformStr)
nickName <- (map["nick_name"], kIntTransformStr)
headPic <- (map["head_pic"], kIntTransformStr)
lastPosition <- (map["last_position"], kIntTransformStr)
groupKey <- (map["group_key"], kIntTransformStr)
groupName <- (map["group_name"], kIntTransformStr)
sosTime <- (map["sos_time"], kIntTransformStr)
}
var displayAddress: String {
let value = lastPosition.trimmed
let parts = value.split(separator: ":", maxSplits: 2, omittingEmptySubsequences: false)
guard parts.count == 3,
Double(parts[0]) != nil,
Double(parts[1]) != nil else { return value }
let address = String(parts[2]).trimmed
return address.isEmpty ? value : address
}
var displayTime: String {
guard let date = parsedSOSDate else { return sosTime }
return Self.displayDateFormatter.string(from: date)
}
private var parsedSOSDate: Date? {
let value = sosTime.trimmed
if let timestamp = TimeInterval(value) {
return Date(timeIntervalSince1970: timestamp > 10_000_000_000 ? timestamp / 1000 : timestamp)
}
for formatter in Self.apiDateFormatters {
if let date = formatter.date(from: value) {
return date
}
}
return ISO8601DateFormatter().date(from: value)
}
private static let apiDateFormatters: [DateFormatter] = [
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm",
"yyyy/MM/dd HH:mm:ss",
"yyyy/MM/dd HH:mm"
].map { format in
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = .current
formatter.dateFormat = format
return formatter
}
private static let displayDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "zh_CN")
formatter.timeZone = .current
formatter.dateFormat = "M月d日 HH:mm"
return formatter
}()
}
final class SOSListViewController: BaseViewController {
private var rootView: SOSListView!
private var items: [SOSListItem] = []
private var isLoading = false
override var isNavigationBarHidden: Bool { true }
override func loadView() {
rootView = SOSListView(frame: UIScreen.main.bounds)
view = rootView
}
override func viewDidLoad() {
super.viewDidLoad()
rootView.tableView.dataSource = self
rootView.tableView.delegate = self
rootView.refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
loadSOSList(showsLoading: true)
}
@objc private func refresh() {
loadSOSList(showsLoading: false)
}
private func loadSOSList(showsLoading: Bool) {
guard !isLoading else { return }
isLoading = true
if showsLoading {
DLToast.showLoading()
}
SystemService.sosList()
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] response in
guard let self else { return }
self.finishLoading()
guard response.code == "0" else {
DLToast.showError(text: response.message ?? "获取求助列表失败")
return
}
self.items = response.list
self.rootView.showEmpty(self.items.isEmpty)
self.rootView.tableView.reloadData()
}, onError: { [weak self] error in
self?.finishLoading()
DLToast.showError(text: error.gatewayMessage ?? error.localizedDescription)
})
.disposed(by: disposeBag)
}
private func finishLoading() {
isLoading = false
DLToast.dismiss()
rootView.refreshControl.endRefreshing()
}
private func showLocation(for item: SOSListItem) {
guard !item.groupKey.isEmpty, !item.lastPosition.isEmpty else { return }
NotificationCenter.default.post(
name: .ShowMemberLocationNotification,
object: nil,
userInfo: [
"last_position": item.lastPosition,
"group_key": item.groupKey,
"user_id": item.userId
]
)
let mainTabBarController = tabBarController as? MainTabBarController
navigationController?.popToRootViewController(animated: true)
mainTabBarController?.selectTab(at: 0)
}
}
extension SOSListViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: SOSListCell = tableView.dequeueReusableCell(for: indexPath)
cell.configure(with: items[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
showLocation(for: items[indexPath.row])
}
}
final class SOSListView: UIView {
let tableView = UITableView(frame: .zero, style: .plain)
let refreshControl = UIRefreshControl()
private let navBackgroundView = UIImageView(image: UIImage(named: "Common/navBar_bg_2"))
private let navView = BaseNavigationView(title: "紧急求助")
private let emptyLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showEmpty(_ isEmpty: Bool) {
emptyLabel.isHidden = !isEmpty
}
private func setupUI() {
backgroundColor = UIColor(hexStr: "#FAFAFA")
navBackgroundView.contentMode = .scaleAspectFill
navBackgroundView.clipsToBounds = true
addSubview(navBackgroundView)
addSubview(navView)
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.showsVerticalScrollIndicator = false
tableView.estimatedRowHeight = 126
tableView.rowHeight = UITableView.automaticDimension
tableView.contentInset = UIEdgeInsets(top: 14, left: 0, bottom: kSafeBottomMargin + 16, right: 0)
tableView.register(SOSListCell.self)
tableView.refreshControl = refreshControl
addSubview(tableView)
emptyLabel.text = "暂无紧急求助"
emptyLabel.textColor = UIColor(hexStr: "#A7ABB2")
emptyLabel.font = .systemFont(ofSize: 15)
emptyLabel.textAlignment = .center
emptyLabel.isHidden = true
addSubview(emptyLabel)
navBackgroundView.translatesAutoresizingMaskIntoConstraints = false
navView.translatesAutoresizingMaskIntoConstraints = false
tableView.translatesAutoresizingMaskIntoConstraints = false
emptyLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
navBackgroundView.topAnchor.constraint(equalTo: topAnchor),
navBackgroundView.leadingAnchor.constraint(equalTo: leadingAnchor),
navBackgroundView.trailingAnchor.constraint(equalTo: trailingAnchor),
navBackgroundView.heightAnchor.constraint(equalToConstant: max(kNaviHeight + 70, 150)),
navView.topAnchor.constraint(equalTo: topAnchor),
navView.leadingAnchor.constraint(equalTo: leadingAnchor),
navView.trailingAnchor.constraint(equalTo: trailingAnchor),
navView.heightAnchor.constraint(equalToConstant: kNaviHeight),
tableView.topAnchor.constraint(equalTo: navView.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: bottomAnchor),
emptyLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
emptyLabel.centerYAnchor.constraint(equalTo: centerYAnchor, constant: -20)
])
}
}
final class SOSListCell: UITableViewCell {
private let cardView = UIView()
private let avatarView = UIImageView()
private let titleLabel = UILabel()
private let addressLabel = UILabel()
private let timeLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
avatarView.image = nil
}
func configure(with item: SOSListItem) {
avatarView.setHeadPic(item.headPic)
titleLabel.attributedText = makeTitle(item)
addressLabel.text = item.displayAddress.isEmpty ? "位置暂未获取" : item.displayAddress
timeLabel.text = item.displayTime
accessibilityLabel = [item.groupName, item.nickName, "紧急求助中", addressLabel.text, timeLabel.text]
.compactMap { $0 }
.filter { !$0.isEmpty }
.joined(separator: "")
}
private func makeTitle(_ item: SOSListItem) -> NSAttributedString {
let groupName = item.groupName.isEmpty ? "当前" : item.groupName
let nickName = item.nickName.isEmpty ? "圈子成员" : item.nickName
let text = "\(groupName)圈子的 \(nickName) 紧急求助中"
let result = NSMutableAttributedString(
string: text,
attributes: [
.font: UIFont.systemFont(ofSize: 16, weight: .regular),
.foregroundColor: UIColor(hexStr: "#273246")
]
)
let highlightColor = UIColor(hexStr: "#04AFFF")
result.addAttribute(.foregroundColor, value: highlightColor, range: (text as NSString).range(of: groupName))
result.addAttribute(.foregroundColor, value: highlightColor, range: (text as NSString).range(of: nickName))
return result
}
private func setupUI() {
backgroundColor = .clear
selectionStyle = .none
contentView.backgroundColor = .clear
cardView.backgroundColor = .white
cardView.layer.cornerRadius = 22
contentView.addSubview(cardView)
avatarView.contentMode = .scaleAspectFill
avatarView.clipsToBounds = true
avatarView.layer.cornerRadius = 25
cardView.addSubview(avatarView)
titleLabel.numberOfLines = 2
titleLabel.setContentCompressionResistancePriority(.required, for: .vertical)
cardView.addSubview(titleLabel)
addressLabel.font = .systemFont(ofSize: 14)
addressLabel.textColor = UIColor(hexStr: "#8D9198")
addressLabel.numberOfLines = 2
cardView.addSubview(addressLabel)
timeLabel.font = .systemFont(ofSize: 13)
timeLabel.textColor = UIColor(hexStr: "#B6B9BE")
cardView.addSubview(timeLabel)
cardView.translatesAutoresizingMaskIntoConstraints = false
avatarView.translatesAutoresizingMaskIntoConstraints = false
titleLabel.translatesAutoresizingMaskIntoConstraints = false
addressLabel.translatesAutoresizingMaskIntoConstraints = false
timeLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cardView.topAnchor.constraint(equalTo: contentView.topAnchor),
cardView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15),
cardView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
cardView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -14),
avatarView.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 15),
avatarView.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 27),
avatarView.widthAnchor.constraint(equalToConstant: 50),
avatarView.heightAnchor.constraint(equalToConstant: 50),
titleLabel.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 22),
titleLabel.leadingAnchor.constraint(equalTo: avatarView.trailingAnchor, constant: 15),
titleLabel.trailingAnchor.constraint(equalTo: cardView.trailingAnchor, constant: -15),
addressLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 6),
addressLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
addressLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor),
timeLabel.topAnchor.constraint(equalTo: addressLabel.bottomAnchor, constant: 7),
timeLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
timeLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor),
timeLabel.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: -16)
])
}
}