962 lines
30 KiB
Swift
962 lines
30 KiB
Swift
//
|
||
// GroupMemberView2.swift
|
||
// QuickLocation
|
||
//
|
||
// Created by 八条 on 2026/8/4.
|
||
//
|
||
|
||
import UIKit
|
||
import Network
|
||
import CoreLocation
|
||
import RxSwift
|
||
import RxCocoa
|
||
import SwiftyUserDefaults
|
||
|
||
class GroupMemberView2: UIView {
|
||
|
||
private var navBarHeightConstraint: NSLayoutConstraint?
|
||
|
||
private static let batteryNormalColor = UIColor(hexStr: "#47DD00")
|
||
private static let batteryLowColor = UIColor(hexStr: "#F85A5D")
|
||
private static let batteryDefaultTextColor = UIColor(hexStr: "#353B4F")
|
||
/// 24pt 图标内腔可用填充宽度(去掉描边与右侧电极)
|
||
private static let batteryFillMaxWidth: CGFloat = 18
|
||
|
||
/// 最大化时展开导航占位为 kNaviHeight,收起/dismiss 时为 0
|
||
func setNavBarExpanded(_ expanded: Bool) {
|
||
navBarHeightConstraint?.constant = expanded ? kNaviHeight : 0
|
||
}
|
||
|
||
func setupMemberInfo(_ model: GroupMemberModel, isOwner: Bool) {
|
||
memberNameLab.text = model.nick_name
|
||
ownView.isHidden = !isOwner
|
||
statusDotView.backgroundColor = UIColor(hexStr: model.is_online ? "#67EA76" : "#D8D8D8")
|
||
statusLab.text = model.is_online ? "在线" : "离线"
|
||
locationLab.text = model.lastLocation
|
||
|
||
let isCurrentUser = model.user_id == AppContextManager.shared.userId
|
||
updateDistance(for: model, isCurrentUser: isCurrentUser)
|
||
|
||
if isCurrentUser {
|
||
setupCurrentUserDeviceInfo()
|
||
} else {
|
||
setupUnknownDeviceInfo(battery: model.battery)
|
||
}
|
||
}
|
||
|
||
private func updateDistance(for model: GroupMemberModel, isCurrentUser: Bool) {
|
||
guard !isCurrentUser else {
|
||
distanceView.isHidden = true
|
||
return
|
||
}
|
||
guard let lat = Defaults[\.currentLatitude],
|
||
let lon = Defaults[\.currentLongitude] else {
|
||
distanceView.isHidden = true
|
||
return
|
||
}
|
||
let meCoord = CLLocationCoordinate2D(latitude: lat, longitude: lon)
|
||
let (otherCoord, _) = CircleMember.parsePosition(model.last_position)
|
||
guard CLLocationCoordinate2DIsValid(meCoord),
|
||
CLLocationCoordinate2DIsValid(otherCoord) else {
|
||
distanceView.isHidden = true
|
||
return
|
||
}
|
||
let meters = CLLocation(latitude: meCoord.latitude, longitude: meCoord.longitude)
|
||
.distance(from: CLLocation(latitude: otherCoord.latitude, longitude: otherCoord.longitude))
|
||
distanceLab.text = Self.formatDistance(meters)
|
||
distanceView.isHidden = false
|
||
}
|
||
|
||
private static func formatDistance(_ meters: CLLocationDistance) -> String {
|
||
if meters < 1000 {
|
||
return "\(Int(meters.rounded()))M"
|
||
}
|
||
return String(format: "%.1fKM", meters / 1000.0)
|
||
}
|
||
|
||
private func setupCurrentUserDeviceInfo() {
|
||
phoneModelIcon.image = UIImage(named: "Home/member_phone")
|
||
phoneModelLab.text = Self.localPhoneModelName()
|
||
phoneModelLab.textColor = Self.batteryDefaultTextColor
|
||
|
||
networkIcon.image = UIImage(named: "Home/member_wifi")
|
||
networkLab.text = Self.localNetworkStatusText()
|
||
networkLab.textColor = Self.batteryDefaultTextColor
|
||
|
||
applyBatteryDisplay(percent: Self.localBatteryPercent())
|
||
}
|
||
|
||
private func setupUnknownDeviceInfo(battery: String) {
|
||
phoneModelIcon.image = UIImage(named: "Home/member_phone_unknown")
|
||
phoneModelLab.text = "***"
|
||
phoneModelLab.textColor = Self.batteryDefaultTextColor
|
||
|
||
networkIcon.image = UIImage(named: "Home/member_network_unknown")
|
||
networkLab.text = "***"
|
||
networkLab.textColor = Self.batteryDefaultTextColor
|
||
|
||
// MQTT / 接口已有电量则展示,否则 unknown
|
||
let percent = Int(battery.int)
|
||
if percent > 0 {
|
||
applyBatteryDisplay(percent: min(percent, 100))
|
||
} else {
|
||
applyBatteryUnknown()
|
||
}
|
||
}
|
||
|
||
private func applyBatteryUnknown() {
|
||
batteryIcon.image = UIImage(named: "Home/member_battery_unknown")
|
||
batteryFillView.isHidden = true
|
||
batteryFillView.layoutChain.width(0)
|
||
batteryLab.text = "***"
|
||
batteryLab.textColor = Self.batteryDefaultTextColor
|
||
}
|
||
|
||
private func applyBatteryDisplay(percent: Int?) {
|
||
guard let percent else {
|
||
applyBatteryUnknown()
|
||
return
|
||
}
|
||
let p = min(max(percent, 0), 100)
|
||
let isLow = p < 20
|
||
let color = isLow ? Self.batteryLowColor : Self.batteryNormalColor
|
||
batteryIcon.image = UIImage(named: isLow ? "Home/member_battery_low" : "Home/member_battery")
|
||
batteryFillView.isHidden = false
|
||
batteryFillView.backgroundColor = color
|
||
batteryFillView.layoutChain.width(Self.batteryFillMaxWidth * CGFloat(p) / 100.0)
|
||
batteryLab.text = "\(p)%"
|
||
batteryLab.textColor = color
|
||
}
|
||
|
||
// MARK: - Local device helpers
|
||
|
||
private static func localPhoneModelName() -> String {
|
||
var systemInfo = utsname()
|
||
uname(&systemInfo)
|
||
let identifier = withUnsafePointer(to: &systemInfo.machine.0) { ptr in
|
||
String(cString: ptr)
|
||
}
|
||
return deviceModelMap[identifier] ?? "iPhone"
|
||
}
|
||
|
||
private static func localBatteryPercent() -> Int? {
|
||
UIDevice.current.isBatteryMonitoringEnabled = true
|
||
let level = UIDevice.current.batteryLevel
|
||
guard level >= 0 else { return nil }
|
||
return Int((level * 100).rounded())
|
||
}
|
||
|
||
private static func localNetworkStatusText() -> String {
|
||
let path = NetworkStatusMonitor.shared.currentPath
|
||
if path.status != .satisfied {
|
||
return "无网络"
|
||
}
|
||
if path.usesInterfaceType(.wifi) {
|
||
return "Wi-Fi"
|
||
}
|
||
if path.usesInterfaceType(.cellular) {
|
||
return "蜂窝"
|
||
}
|
||
return "无网络"
|
||
}
|
||
|
||
private static let deviceModelMap: [String: String] = [
|
||
"iPhone10,1": "iPhone 8",
|
||
"iPhone10,4": "iPhone 8",
|
||
"iPhone10,2": "iPhone 8 Plus",
|
||
"iPhone10,5": "iPhone 8 Plus",
|
||
// iPhone X系列
|
||
"iPhone10,3": "iPhone X",
|
||
"iPhone10,6": "iPhone X",
|
||
"iPhone11,2": "iPhone XS",
|
||
"iPhone11,4": "iPhone XS Max",
|
||
"iPhone11,6": "iPhone XS Max",
|
||
"iPhone11,8": "iPhone XR",
|
||
// iPhone11
|
||
"iPhone12,1": "iPhone 11",
|
||
"iPhone12,3": "iPhone 11 Pro",
|
||
"iPhone12,5": "iPhone 11 Pro Max",
|
||
// iPhone12
|
||
"iPhone13,1": "iPhone 12 mini",
|
||
"iPhone13,2": "iPhone 12",
|
||
"iPhone13,3": "iPhone 12 Pro",
|
||
"iPhone13,4": "iPhone 12 Pro Max",
|
||
// iPhone13
|
||
"iPhone14,4": "iPhone 13 mini",
|
||
"iPhone14,5": "iPhone 13",
|
||
"iPhone14,2": "iPhone 13 Pro",
|
||
"iPhone14,3": "iPhone 13 Pro Max",
|
||
// iPhone14
|
||
"iPhone14,7": "iPhone 14",
|
||
"iPhone14,8": "iPhone 14 Plus",
|
||
"iPhone15,2": "iPhone 14 Pro",
|
||
"iPhone15,3": "iPhone 14 Pro Max",
|
||
// iPhone15
|
||
"iPhone15,4": "iPhone 15",
|
||
"iPhone15,5": "iPhone 15 Plus",
|
||
"iPhone16,1": "iPhone 15 Pro",
|
||
"iPhone16,2": "iPhone 15 Pro Max",
|
||
// iPhone16
|
||
"iPhone17,1": "iPhone 16 Pro",
|
||
"iPhone17,2": "iPhone 16 Pro Max",
|
||
"iPhone17,3": "iPhone 16",
|
||
"iPhone17,4": "iPhone 16 Plus",
|
||
"iPhone17,5": "iPhone 16e",
|
||
// iPhone17 2025机型
|
||
"iPhone18,1": "iPhone 17 Pro",
|
||
"iPhone18,2": "iPhone 17 Pro Max",
|
||
"iPhone18,3": "iPhone 17",
|
||
"iPhone18,4": "iPhone Air",
|
||
// Simulator
|
||
"i386": "Simulator",
|
||
"x86_64": "Simulator",
|
||
"arm64": "Simulator"
|
||
]
|
||
|
||
private func setupRx() {
|
||
|
||
}
|
||
|
||
private func setupUI() {
|
||
addSubview(headerBgView)
|
||
addSubview(navBarView)
|
||
addSubview(scrollView)
|
||
|
||
headerBgView.layoutChain
|
||
.edges(excludingEdge: .bottom)
|
||
.heightToWidth(160/375)
|
||
|
||
navBarView.layoutChain
|
||
.edges(excludingEdge: .bottom)
|
||
.height(0)
|
||
navBarHeightConstraint = navBarView.jh_constraint(
|
||
.height, toAttribute: .notAnAttribute, otherView: nil, relation: .equal
|
||
)
|
||
|
||
scrollView.layoutChain
|
||
.topToBottomOfView(navBarView)
|
||
.edges(excludingEdge: .top)
|
||
scrollView.isScrollEnabled = false
|
||
|
||
let lineView = UIView()
|
||
lineView.backgroundColor = .black.withAlphaComponent(0.2)
|
||
lineView.cornerRadius = 2
|
||
addSubview(lineView)
|
||
lineView.layoutChain
|
||
.top(9)
|
||
.centerX()
|
||
.width(40)
|
||
.height(4)
|
||
}
|
||
|
||
lazy var headerBgView: UIImageView = {
|
||
let view = UIImageView()
|
||
view.image = UIImage(named: "Home/group_member_bg2")
|
||
view.backgroundColor = .clear
|
||
view.contentMode = .scaleAspectFill
|
||
return view
|
||
}()
|
||
|
||
lazy var navBarView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
view.clipsToBounds = true
|
||
|
||
let icon = UIImageView(image: UIImage(named: "Home/member_logo"))
|
||
view.addSubview(icon)
|
||
icon.layoutChain
|
||
.top(kStatusBarHeight + 12)
|
||
.left(30)
|
||
|
||
view.addSubview(inviteBtn)
|
||
inviteBtn.layoutChain
|
||
.centerY(icon)
|
||
.right(15)
|
||
.width(32)
|
||
.heightToWidth(1)
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var inviteBtn: UIButton = {
|
||
let btn = UIButton(type: .custom)
|
||
btn.setImage(UIImage(named: "Home/member_invite"), for: .normal)
|
||
btn.extendEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 10, right: 15)
|
||
return btn
|
||
}()
|
||
|
||
lazy var scrollView: UIScrollView = {
|
||
let view = UIScrollView()
|
||
view.backgroundColor = .clear
|
||
view.showsHorizontalScrollIndicator = false
|
||
view.bounces = false
|
||
|
||
view.addSubview(scrollContentView)
|
||
scrollContentView.layoutChain.edges().widthToView(view)
|
||
|
||
scrollContentView.addSubview(memberView)
|
||
memberView.layoutChain
|
||
.edges(excludingEdge: .bottom)
|
||
|
||
scrollContentView.addSubview(todayTrackView)
|
||
todayTrackView.layoutChain
|
||
.topToBottomOfView(memberView, offset: 16)
|
||
.edges(excludingEdge: .top)
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var scrollContentView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
return view
|
||
}()
|
||
|
||
// MARK: - 成员
|
||
lazy var memberView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
|
||
let icon = UIImageView()
|
||
icon.image = UIImage(named: "Home/group_name_icon")
|
||
view.addSubview(icon)
|
||
icon.layoutChain
|
||
.top(23)
|
||
.left(15)
|
||
.width(18)
|
||
.heightToWidth(1)
|
||
|
||
view.addSubview(groupNameLab)
|
||
groupNameLab.layoutChain
|
||
.leftToRightOfView(icon, offset: 6)
|
||
.centerY(icon)
|
||
|
||
view.addSubview(changeGroupBtn)
|
||
changeGroupBtn.layoutChain
|
||
.centerY(icon)
|
||
.right(15)
|
||
.height(20)
|
||
|
||
let memberView = UIView()
|
||
memberView.backgroundColor = .white
|
||
memberView.cornerRadius = 20
|
||
memberView.addSubview(memberCV)
|
||
view.addSubview(memberView)
|
||
|
||
memberView.layoutChain
|
||
.topToBottomOfView(icon, offset: 12)
|
||
.edgesHorzontal(15)
|
||
.height(100)
|
||
|
||
memberCV.layoutChain
|
||
.edges(all: 5, excludingEdge: .bottom)
|
||
.bottom()
|
||
|
||
view.addSubview(memberInfoView)
|
||
memberInfoView.layoutChain
|
||
.topToBottomOfView(memberView, offset: 12)
|
||
.edgesHorzontal(15)
|
||
.height(150)
|
||
.bottom()
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var groupNameLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = " "
|
||
label.font = .systemFont(ofSize: 16, weight: .bold)
|
||
label.textColor = UIColor(hexStr: "#353B4F")
|
||
return label
|
||
}()
|
||
|
||
lazy var changeGroupBtn: UIButton = {
|
||
let btn = UIButton(type: .custom)
|
||
btn.setTitle("切换圈子 ", for: .normal)
|
||
btn.setTitleColor(UIColor(hexStr: "#353B4F"), for: .normal)
|
||
btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
|
||
btn.setImage(UIImage(named: "Common/arrow_right"), for: .normal)
|
||
btn.semanticContentAttribute = .forceRightToLeft
|
||
btn.extendEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 10, right: 15)
|
||
return btn
|
||
}()
|
||
|
||
/// 成员列表
|
||
lazy var memberCV: UICollectionView = {
|
||
let layout = UICollectionViewFlowLayout()
|
||
layout.itemSize = CGSize(width: 61, height: 95)
|
||
layout.minimumLineSpacing = 15
|
||
layout.sectionInset = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
|
||
layout.scrollDirection = .horizontal
|
||
|
||
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||
cv.backgroundColor = .clear
|
||
cv.showsHorizontalScrollIndicator = false
|
||
cv.register(GroupMemberListCell.self)
|
||
return cv
|
||
}()
|
||
|
||
/// 成员信息
|
||
lazy var memberInfoView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .white
|
||
view.cornerRadius = 20
|
||
|
||
view.addSubview(memberNameLab)
|
||
memberNameLab.layoutChain
|
||
.top(20)
|
||
.left(18)
|
||
|
||
view.addSubview(tagView)
|
||
tagView.layoutChain
|
||
.leftToRightOfView(memberNameLab, offset: 6)
|
||
.centerY(memberNameLab)
|
||
.height(20)
|
||
ownView.layoutChain.height(16)
|
||
|
||
let statusView = UIView()
|
||
statusView.backgroundColor = .clear
|
||
statusView.addSubview(statusDotView)
|
||
statusView.addSubview(statusLab)
|
||
view.addSubview(statusView)
|
||
statusView.layoutChain
|
||
.right(18)
|
||
.centerY(memberNameLab)
|
||
|
||
statusDotView.layoutChain
|
||
.left()
|
||
.width(8)
|
||
.heightToWidth(1)
|
||
.centerY()
|
||
|
||
statusLab.layoutChain
|
||
.leftToRightOfView(statusDotView, offset: 4)
|
||
.edgesVertical()
|
||
.right()
|
||
|
||
let locationIcon = UIImageView()
|
||
locationIcon.image = UIImage(named: "Home/member_location")
|
||
view.addSubview(locationIcon)
|
||
locationIcon.layoutChain
|
||
.topToBottomOfView(memberNameLab, offset: 17)
|
||
.left(20)
|
||
.width(16)
|
||
.heightToWidth(1)
|
||
|
||
view.addSubview(locationLab)
|
||
locationLab.layoutChain
|
||
.centerY(locationIcon)
|
||
.leftToRightOfView(locationIcon, offset: 7)
|
||
.compressionHorizontal(.defaultLow)
|
||
|
||
view.addSubview(distanceView)
|
||
distanceView.layoutChain
|
||
.right(18)
|
||
.centerY(locationLab)
|
||
.compressionHorizontal(.required)
|
||
|
||
locationLab.layoutChain.rightToLeftOfView(distanceView, offset: -30, relation: .lessThanOrEqual)
|
||
|
||
view.addSubview(phoneModelView)
|
||
view.addSubview(batteryView)
|
||
view.addSubview(networkView)
|
||
view.addSubview(weatherView)
|
||
|
||
phoneModelView.layoutChain
|
||
.topToBottomOfView(locationLab, offset: 20)
|
||
.left()
|
||
|
||
batteryView.layoutChain
|
||
.topToView(phoneModelView)
|
||
.leftToRightOfView(phoneModelView)
|
||
.widthToView(phoneModelView)
|
||
|
||
networkView.layoutChain
|
||
.topToView(phoneModelView)
|
||
.leftToRightOfView(batteryView)
|
||
.widthToView(phoneModelView)
|
||
|
||
weatherView.layoutChain
|
||
.topToView(phoneModelView)
|
||
.leftToRightOfView(networkView)
|
||
.right()
|
||
.widthToView(phoneModelView)
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var memberNameLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = " "
|
||
label.font = FontManager.youSheBiaoTiHei(12)
|
||
label.textColor = UIColor(hexStr: "#353B4F")
|
||
return label
|
||
}()
|
||
|
||
lazy var tagView: UIStackView = {
|
||
let view = UIStackView(arrangedSubviews: [ownView])
|
||
view.axis = .horizontal
|
||
view.alignment = .center
|
||
view.spacing = 5
|
||
view.backgroundColor = .clear
|
||
return view
|
||
}()
|
||
|
||
// 圈主
|
||
lazy var ownView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = UIColor(hexStr: "#E3F6FF")
|
||
view.cornerRadius = 6
|
||
view.isHidden = true
|
||
|
||
let label = UILabel()
|
||
label.textColor = UIColor(hexStr: "#128CC6")
|
||
label.font = .systemFont(ofSize: 10, weight: .bold)
|
||
label.text = "圈主"
|
||
|
||
view.addSubview(label)
|
||
label.layoutChain
|
||
.edgesVertical(2)
|
||
.edgesHorzontal(8)
|
||
|
||
return view
|
||
}()
|
||
|
||
/// 在线状态
|
||
lazy var statusDotView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = UIColor(hexStr: "#D8D8D8")
|
||
view.cornerRadius = 4
|
||
return view
|
||
}()
|
||
|
||
lazy var statusLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = " "
|
||
label.font = .systemFont(ofSize: 10, weight: .medium)
|
||
label.textColor = UIColor(hexStr: "#353B4F")
|
||
return label
|
||
}()
|
||
|
||
// 位置
|
||
lazy var locationLab: UILabel = {
|
||
let label = UILabel()
|
||
label.textColor = UIColor(hexStr: "#767676")
|
||
label.font = .systemFont(ofSize: 12, weight: .medium)
|
||
return label
|
||
}()
|
||
|
||
// 距离
|
||
lazy var distanceView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = UIColor(hexStr: "#353B4F")
|
||
view.cornerRadius = 6
|
||
view.isHidden = true
|
||
|
||
let label = UILabel()
|
||
label.textColor = UIColor(hexStr: "#58EDFF")
|
||
label.font = FontManager.youSheBiaoTiHei(10)
|
||
label.text = "距离"
|
||
|
||
view.addSubview(label)
|
||
label.layoutChain
|
||
.left(5)
|
||
.edgesVertical(3)
|
||
|
||
view.addSubview(distanceLab)
|
||
distanceLab.layoutChain
|
||
.leftToRightOfView(label, offset: 2)
|
||
.right(5)
|
||
.centerY()
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var distanceLab: UILabel = {
|
||
let label = UILabel()
|
||
label.textColor = .white
|
||
label.font = FontManager.youSheBiaoTiHei(10)
|
||
return label
|
||
}()
|
||
|
||
/// 手机型号
|
||
lazy var phoneModelView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
|
||
view.addSubview(phoneModelIcon)
|
||
phoneModelIcon.layoutChain
|
||
.top()
|
||
.centerX()
|
||
.width(24)
|
||
.height(24)
|
||
|
||
view.addSubview(phoneModelLab)
|
||
phoneModelLab.layoutChain
|
||
.topToBottomOfView(phoneModelIcon, offset: 5)
|
||
.edgesHorzontal(5)
|
||
.bottom()
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var phoneModelIcon: UIImageView = {
|
||
let view = UIImageView(image: UIImage(named: "Home/member_phone_unknown"))
|
||
view.contentMode = .scaleAspectFit
|
||
return view
|
||
}()
|
||
|
||
lazy var phoneModelLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = "***"
|
||
label.textColor = UIColor(hexStr: "#353B4F")
|
||
label.font = .systemFont(ofSize: 12, weight: .medium)
|
||
label.textAlignment = .center
|
||
label.numberOfLines = 0
|
||
return label
|
||
}()
|
||
|
||
/// 电量
|
||
lazy var batteryView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
|
||
view.addSubview(batteryFillView)
|
||
view.addSubview(batteryIcon)
|
||
batteryIcon.layoutChain
|
||
.top()
|
||
.centerX()
|
||
.width(24)
|
||
.height(24)
|
||
|
||
// 填充层叠在轮廓下方,内边距避开描边与右侧电极
|
||
batteryFillView.layoutChain
|
||
.centerY(batteryIcon)
|
||
.leftToView(batteryIcon, offset: 2)
|
||
.height(12)
|
||
.width(0)
|
||
|
||
view.addSubview(batteryLab)
|
||
batteryLab.layoutChain
|
||
.topToBottomOfView(batteryIcon, offset: 5)
|
||
.centerX()
|
||
.bottom()
|
||
return view
|
||
}()
|
||
|
||
lazy var batteryFillView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = Self.batteryNormalColor
|
||
view.isHidden = true
|
||
return view
|
||
}()
|
||
|
||
lazy var batteryIcon: UIImageView = {
|
||
let view = UIImageView(image: UIImage(named: "Home/member_battery_unknown"))
|
||
view.contentMode = .scaleAspectFit
|
||
return view
|
||
}()
|
||
|
||
lazy var batteryLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = "***"
|
||
label.textColor = UIColor(hexStr: "#353B4F")
|
||
label.font = .systemFont(ofSize: 12, weight: .medium)
|
||
return label
|
||
}()
|
||
|
||
/// 手机网络
|
||
lazy var networkView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
|
||
view.addSubview(networkIcon)
|
||
networkIcon.layoutChain
|
||
.top()
|
||
.centerX()
|
||
.width(24)
|
||
.height(24)
|
||
|
||
view.addSubview(networkLab)
|
||
networkLab.layoutChain
|
||
.topToBottomOfView(networkIcon, offset: 5)
|
||
.centerX()
|
||
.bottom()
|
||
return view
|
||
}()
|
||
|
||
lazy var networkIcon: UIImageView = {
|
||
let view = UIImageView(image: UIImage(named: "Home/member_network_unknown"))
|
||
view.contentMode = .scaleAspectFit
|
||
return view
|
||
}()
|
||
|
||
lazy var networkLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = "***"
|
||
label.textColor = UIColor(hexStr: "#353B4F")
|
||
label.font = .systemFont(ofSize: 12, weight: .medium)
|
||
return label
|
||
}()
|
||
|
||
/// 天气
|
||
lazy var weatherView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
|
||
let icon = UIImageView(image: UIImage(named: "Home/member_weather_unknown"))
|
||
view.addSubview(icon)
|
||
icon.layoutChain
|
||
.top()
|
||
.centerX()
|
||
.width(24)
|
||
.height(24)
|
||
|
||
view.addSubview(weatherLab)
|
||
weatherLab.layoutChain
|
||
.topToBottomOfView(icon, offset: 5)
|
||
.centerX()
|
||
.bottom()
|
||
return view
|
||
}()
|
||
|
||
lazy var weatherLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = "***"
|
||
label.textColor = UIColor(hexStr: "#353B4F")
|
||
label.font = .systemFont(ofSize: 12, weight: .medium)
|
||
return label
|
||
}()
|
||
|
||
// MARK: - 今日轨迹
|
||
lazy var todayTrackView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
|
||
let icon = UIImageView()
|
||
icon.image = UIImage(named: "Home/member_today_track")
|
||
view.addSubview(icon)
|
||
icon.layoutChain
|
||
.top()
|
||
.left(15)
|
||
.width(18)
|
||
.heightToWidth(1)
|
||
|
||
let titleLab = UILabel()
|
||
titleLab.text = "今日轨迹"
|
||
titleLab.textColor = UIColor(hexStr: "#293445")
|
||
titleLab.font = .systemFont(ofSize: 16, weight: .bold)
|
||
view.addSubview(titleLab)
|
||
titleLab.layoutChain
|
||
.leftToRightOfView(icon, offset: 6)
|
||
.centerY(icon)
|
||
|
||
let infoView = UIView()
|
||
infoView.backgroundColor = .white
|
||
infoView.cornerRadius = 16
|
||
view.addSubview(infoView)
|
||
infoView.layoutChain
|
||
.topToBottomOfView(icon, offset: 12)
|
||
.edgesHorzontal(15)
|
||
.bottom()
|
||
|
||
let mapIcon = UIImageView(image: UIImage(named: "Home/member_map"))
|
||
infoView.addSubview(mapIcon)
|
||
mapIcon.layoutChain
|
||
.left(12)
|
||
.edgesVertical(15)
|
||
.width(80)
|
||
.heightToWidth(1)
|
||
|
||
let locationIcon = UIImageView(image: UIImage(named: "Home/member_location"))
|
||
infoView.addSubview(locationIcon)
|
||
locationIcon.layoutChain
|
||
.topToView(mapIcon, offset: 11)
|
||
.leftToRightOfView(mapIcon, offset: 14)
|
||
.width(16)
|
||
.heightToWidth(1)
|
||
|
||
let stayTxtLab = UILabel()
|
||
stayTxtLab.text = "今天停留了"
|
||
stayTxtLab.textColor = UIColor(hexStr: "#293445")
|
||
stayTxtLab.font = .systemFont(ofSize: 16, weight: .medium)
|
||
view.addSubview(stayTxtLab)
|
||
stayTxtLab.layoutChain
|
||
.leftToRightOfView(locationIcon, offset: 6)
|
||
.centerY(locationIcon)
|
||
|
||
let stayNumView = UIView()
|
||
stayNumView.backgroundColor = UIColor(hexStr: "#293445")
|
||
stayNumView.cornerRadius = 6
|
||
infoView.addSubview(stayNumView)
|
||
stayNumView.layoutChain
|
||
.leftToRightOfView(stayTxtLab)
|
||
.centerY(stayTxtLab)
|
||
|
||
stayNumView.addSubview(stayNumLab)
|
||
stayNumLab.layoutChain
|
||
.edgesHorzontal(5)
|
||
.edgesVertical()
|
||
|
||
let stayTxtLab1 = UILabel()
|
||
stayTxtLab1.text = "个地方"
|
||
stayTxtLab1.textColor = UIColor(hexStr: "#293445")
|
||
stayTxtLab1.font = .systemFont(ofSize: 16, weight: .medium)
|
||
view.addSubview(stayTxtLab1)
|
||
stayTxtLab1.layoutChain
|
||
.leftToRightOfView(stayNumView, offset: 0)
|
||
.centerY(stayTxtLab)
|
||
|
||
view.addSubview(stayLocationView)
|
||
stayLocationView.layoutChain
|
||
.topToBottomOfView(locationIcon, offset: 12)
|
||
.leftToRightOfView(mapIcon, offset: 14)
|
||
.right()
|
||
.bottomToView(mapIcon)
|
||
|
||
view.addSubview(stayNodataView)
|
||
stayNodataView.layoutChain
|
||
.topToBottomOfView(stayTxtLab, offset: 14)
|
||
.leftToView(stayTxtLab)
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var stayNumLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = "0"
|
||
label.textColor = UIColor(hexStr: "#58EDFF")
|
||
label.font = FontManager.youSheBiaoTiHei(14)
|
||
return label
|
||
}()
|
||
|
||
lazy var stayLocationView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
view.isHidden = true
|
||
|
||
let nowDotView = UIView()
|
||
nowDotView.backgroundColor = UIColor(hexStr: "#3DC0FF")
|
||
nowDotView.cornerRadius = 6
|
||
view.addSubview(nowDotView)
|
||
nowDotView.layoutChain
|
||
.top()
|
||
.left()
|
||
.width(12)
|
||
.heightToWidth(1)
|
||
|
||
let dotView = UIView()
|
||
dotView.backgroundColor = .white
|
||
dotView.cornerRadius = 3
|
||
nowDotView.addSubview(dotView)
|
||
dotView.layoutChain
|
||
.centerX()
|
||
.centerY()
|
||
.width(6)
|
||
.heightToWidth(1)
|
||
|
||
let dashLine = DashLineView()
|
||
dashLine.backgroundColor = .clear
|
||
view.addSubview(dashLine)
|
||
dashLine.layoutChain
|
||
.topToBottomOfView(nowDotView)
|
||
.centerX(nowDotView)
|
||
.width(1)
|
||
.bottom()
|
||
|
||
view.addSubview(stayLocationLab)
|
||
stayLocationLab.layoutChain
|
||
.leftToRightOfView(nowDotView, offset: 3)
|
||
.centerY(nowDotView)
|
||
.right(15)
|
||
|
||
let stayStatusView = UIView()
|
||
stayStatusView.backgroundColor = UIColor(hexStr: "#F2F2F2")
|
||
stayStatusView.cornerRadius = 6
|
||
view.addSubview(stayStatusView)
|
||
stayStatusView.layoutChain
|
||
.topToBottomOfView(stayLocationLab, offset: 9)
|
||
.leftToView(stayLocationLab)
|
||
|
||
let clockIcon = UIImageView(image: UIImage(named: "Home/member_clock"))
|
||
stayStatusView.addSubview(clockIcon)
|
||
clockIcon.layoutChain
|
||
.edges(all: 4, excludingEdge: .right)
|
||
.width(12)
|
||
.heightToWidth(1)
|
||
|
||
stayStatusView.addSubview(stayStatusLab)
|
||
stayStatusLab.layoutChain
|
||
.leftToRightOfView(clockIcon, offset: 2)
|
||
.right(7)
|
||
.centerY()
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var stayLocationLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = "当前 xxx 附近"
|
||
label.textColor = UIColor(hexStr: "#293445")
|
||
label.font = .systemFont(ofSize: 10, weight: .bold)
|
||
return label
|
||
}()
|
||
|
||
lazy var stayStatusLab: UILabel = {
|
||
let label = UILabel()
|
||
label.text = "停留中..." // 停留多少时间
|
||
label.textColor = UIColor(hexStr: "#293445")
|
||
label.font = .systemFont(ofSize: 8, weight: .bold)
|
||
return label
|
||
}()
|
||
|
||
lazy var stayNodataView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = UIColor(hexStr: "#F2F2F2")
|
||
view.cornerRadius = 6
|
||
view.isHidden = false
|
||
|
||
let label = UILabel()
|
||
label.text = "暂时没有停留记录"
|
||
label.textColor = UIColor(hexStr: "#293445")
|
||
label.font = .systemFont(ofSize: 12, weight: .medium)
|
||
view.addSubview(label)
|
||
label.layoutChain
|
||
.edgesHorzontal(6)
|
||
.edgesVertical(9)
|
||
|
||
return view
|
||
}()
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
backgroundColor = UIColor(hexStr: "#FAFAFA")
|
||
cornerRadius = 30
|
||
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||
_ = NetworkStatusMonitor.shared
|
||
setupUI()
|
||
setupRx()
|
||
}
|
||
|
||
required init?(coder aDecoder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
}
|
||
|
||
/// 轻量网络状态监听,供面板读取当前路径
|
||
private final class NetworkStatusMonitor {
|
||
static let shared = NetworkStatusMonitor()
|
||
|
||
private let monitor = NWPathMonitor()
|
||
private(set) var currentPath: NWPath
|
||
|
||
private init() {
|
||
currentPath = monitor.currentPath
|
||
monitor.pathUpdateHandler = { [weak self] path in
|
||
self?.currentPath = path
|
||
}
|
||
monitor.start(queue: DispatchQueue(label: "com.quicklocation.networkstatus"))
|
||
}
|
||
}
|