jsdw_ios/QuickLocation/Section/Home/GroupMemberView2.swift

1186 lines
38 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.

//
// GroupMemberView2.swift
// QuickLocation
//
// Created by on 2026/8/4.
//
import UIKit
import Network
import CoreLocation
import SwiftyUserDefaults
class GroupMemberView2: UIView {
private var navBarHeightConstraint: NSLayoutConstraint?
private var todayTrackTopToInteraction: NSLayoutConstraint?
private var todayTrackTopToMember: NSLayoutConstraint?
private var currentMemberModel: GroupMemberModel?
private var emojiEchoByUserId: [String: String] = [:]
private var messageEchoByUserId: [String: String] = [:]
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,
phoneUsage: PhoneUsageTodayModel?
) {
currentMemberModel = model
memberNameLab.text = model.nick_name
ownView.isHidden = !isOwner
relationIconView.configure(relationIdx: model.extra.relation_idx)
updateMoodBadge(model.mood)
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)
applyPhoneUsage(phoneUsage)
updateRealtimeInteractionVisibility(isCurrentUser: isCurrentUser)
refreshRealtimeInteraction()
}
func updatePhoneUsage(_ phoneUsage: PhoneUsageTodayModel?, for userId: String) {
guard currentMemberModel?.user_id == userId else { return }
applyPhoneUsage(phoneUsage)
}
func applyEmojiEcho(name: String, for userId: String) {
emojiEchoByUserId[userId] = name
if currentMemberModel?.user_id == userId {
refreshRealtimeInteraction()
}
}
func applyMessageEcho(text: String, for userId: String) {
messageEchoByUserId[userId] = text
if currentMemberModel?.user_id == userId {
refreshRealtimeInteraction()
}
}
private func refreshRealtimeInteraction() {
let userId = currentMemberModel?.user_id ?? ""
applyEmojiDisplay(emojiEchoByUserId[userId])
applyMessageDisplay(messageEchoByUserId[userId])
}
private func updateRealtimeInteractionVisibility(isCurrentUser: Bool) {
let show = !isCurrentUser
interactionView.isHidden = !show
if show {
todayTrackTopToMember?.isActive = false
todayTrackTopToInteraction?.isActive = true
} else {
todayTrackTopToInteraction?.isActive = false
todayTrackTopToMember?.isActive = true
}
}
private func applyEmojiDisplay(_ name: String?) {
if let name, let image = InteractionEmojiCell.pngImage(named: name) {
emojiImageView.image = image
emojiImageView.tintColor = nil
return
}
let config = UIImage.SymbolConfiguration(pointSize: 36, weight: .regular)
emojiImageView.image = UIImage(systemName: "face.smiling", withConfiguration: config)
emojiImageView.tintColor = UIColor(hexStr: "#C8CDD6")
}
private func applyMessageDisplay(_ text: String?) {
messageTextLab.text = text ?? "暂时没有消息记录"
}
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 updateMoodBadge(_ mood: Int) {
let image = MoodStatusCatalog.item(moodIndex: mood)?.pngImage
moodImageView.image = image
moodImageView.isHidden = image == nil
}
private func applyPhoneUsage(_ usage: PhoneUsageTodayModel?) {
applyDeviceInfo(usage?.phoneInfo)
phoneReportView.configure(with: Self.phoneReportPreview(from: usage))
applyStayPoints(usage?.stayPoints ?? [])
}
private func applyDeviceInfo(_ info: PhoneUsageInfoModel?) {
let model = info?.model.trimmed ?? ""
let hasModel = !model.isEmpty && model != "未知"
phoneModelIcon.image = UIImage(named: hasModel ? "Home/member_phone" : "Home/member_phone_unknown")
phoneModelLab.text = hasModel ? model : "***"
phoneModelLab.textColor = Self.batteryDefaultTextColor
let network = info?.network.trimmed ?? ""
let hasNetwork = !network.isEmpty && network != "未知"
networkIcon.image = UIImage(named: hasNetwork ? "Home/member_wifi" : "Home/member_network_unknown")
networkLab.text = hasNetwork ? network : "***"
networkLab.textColor = Self.batteryDefaultTextColor
applyBatteryDisplay(percent: Self.percent(from: info?.battery))
let weather = info?.weather.trimmed ?? ""
guard !weather.isEmpty else {
applyUnknownWeather(text: "***")
return
}
let snapshot = MemberWeatherService.snapshot(reportedText: weather)
weatherIcon.image = UIImage(named: snapshot.assetName)
weatherLab.text = snapshot.text
}
private func applyUnknownWeather(text: String) {
weatherIcon.image = UIImage(named: "Home/member_weather_unknown")
weatherLab.text = text
}
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
}
private static func percent(from value: String?) -> Int? {
guard let value else { return nil }
let digits = value.filter(\.isNumber)
guard let percent = Int(digits) else { return nil }
return min(max(percent, 0), 100)
}
private static func phoneReportPreview(from usage: PhoneUsageTodayModel?) -> MemberPhoneReportPreview? {
guard let usage else { return nil }
let info = usage.phoneInfo
return MemberPhoneReportPreview(
screenTimeSeconds: screenTimeSeconds(from: info?.useTime),
usageCount: usage.appCount,
unlockCount: Int(info?.unlockCount.trimmed ?? "")
)
}
private static func screenTimeSeconds(from value: String?) -> Int? {
guard let value else { return nil }
let text = value.trimmed
guard !text.isEmpty else { return nil }
let hours = firstInteger(in: text, before: "小时") ?? 0
let minutes: Int
if text.contains("分钟") {
minutes = firstInteger(in: text, before: "分钟") ?? 0
} else {
minutes = firstInteger(in: text, before: "") ?? 0
}
guard text.contains("小时") || text.contains("") else { return nil }
return max(0, hours * 3600 + minutes * 60)
}
private static func firstInteger(in text: String, before suffix: String) -> Int? {
guard let range = text.range(of: suffix) else { return nil }
let prefix = text[..<range.lowerBound]
let digits = prefix.reversed().prefix(while: \.isNumber).reversed()
return Int(String(digits))
}
private func applyStayPoints(_ stayPoints: [PhoneUsageStayPointModel]) {
stayNumLab.text = "\(stayPoints.count)"
guard let latest = stayPoints.max(by: {
($0.startDate ?? .distantPast) < ($1.startDate ?? .distantPast)
}) else {
stayLocationView.isHidden = true
stayNodataView.isHidden = false
stayLocationLab.text = ""
stayStatusLab.text = ""
return
}
let street = latest.address?.street.trimmed ?? ""
let formattedAddress = latest.address?.formattedAddress.trimmed ?? ""
stayLocationLab.text = !street.isEmpty ? street : (!formattedAddress.isEmpty ? formattedAddress : "未知地点")
stayStatusLab.text = Self.stayDurationText(minutes: latest.resolvedDurationMinutes)
stayLocationView.isHidden = false
stayNodataView.isHidden = true
}
private static func stayDurationText(minutes: Int?) -> String {
guard let minutes else { return "停留时长未知" }
let value = max(0, minutes)
let hours = value / 60
let remainingMinutes = value % 60
if hours > 0, remainingMinutes > 0 {
return "停留\(hours)小时\(remainingMinutes)"
}
if hours > 0 {
return "停留\(hours)小时"
}
return "停留\(remainingMinutes)分钟"
}
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.showsVerticalScrollIndicator = false
view.bounces = false
view.addSubview(scrollContentView)
scrollContentView.layoutChain.edges().widthToView(view)
scrollContentView.addSubview(memberView)
memberView.layoutChain
.edges(excludingEdge: .bottom)
scrollContentView.addSubview(interactionView)
interactionView.isHidden = true
interactionView.layoutChain
.topToBottomOfView(memberView, offset: 12)
.edgesHorzontal()
scrollContentView.addSubview(todayTrackView)
todayTrackView.layoutChain
.topToBottomOfView(interactionView, offset: 15)
todayTrackTopToInteraction = todayTrackView.jh_lastConstraint
todayTrackTopToInteraction?.isActive = false
todayTrackView.layoutChain
.edgesHorzontal()
todayTrackTopToMember = todayTrackView.jh_pinEdge(.top, toEdge: .bottom, of: memberView, offset: 12)
scrollContentView.addSubview(phoneReportView)
phoneReportView.layoutChain
.topToBottomOfView(todayTrackView, offset: 15)
.edgesHorzontal()
.bottom(49 + kSafeBottomMargin)
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)
let groupBtnView = UIView()
groupBtnView.backgroundColor = .white
groupBtnView.cornerRadius = 8
view.addSubview(groupBtnView)
groupBtnView.addSubview(changeGroupBtn)
groupBtnView.layoutChain
.centerY(icon)
.right(15)
.width(70)
.height(24)
changeGroupBtn.layoutChain
.edges()
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)
moodImageView.layoutChain.width(20).height(20)
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(14)
label.textColor = UIColor(hexStr: "#293445")
return label
}()
lazy var tagView: UIStackView = {
let view = UIStackView(arrangedSubviews: [ownView, relationIconView, moodImageView])
view.axis = .horizontal
view.alignment = .center
view.spacing = 5
view.backgroundColor = .clear
return view
}()
lazy var moodImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.isHidden = true
return imageView
}()
lazy var relationIconView: RelationIconImageView = {
RelationIconImageView()
}()
//
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
view.addSubview(weatherIcon)
weatherIcon.layoutChain
.top()
.centerX()
.width(24)
.height(24)
view.addSubview(weatherLab)
weatherLab.layoutChain
.topToBottomOfView(weatherIcon, offset: 5)
.centerX()
.bottom()
return view
}()
lazy var weatherIcon: UIImageView = {
let view = UIImageView(image: UIImage(named: "Home/member_weather_unknown"))
view.contentMode = .scaleAspectFit
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: - / APP使
lazy var phoneReportView = MemberPhoneReportView()
/// UI scroll
lazy var appUsageView: MemberAppUsageView = {
let view = MemberAppUsageView()
view.isHidden = true
return view
}()
// MARK: -
lazy var interactionView: UIView = {
let view = UIView()
view.backgroundColor = .clear
let icon = UIImageView()
icon.image = UIImage(named: "Home/member_interaction")
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)
view.addSubview(emojiCard)
view.addSubview(messageCard)
emojiCard.layoutChain
.topToBottomOfView(icon, offset: 12)
.left(16)
.height(58)
.bottom()
messageCard.layoutChain
.topToView(emojiCard)
.leftToRightOfView(emojiCard, offset: 10)
.right(16)
.widthToView(emojiCard)
.bottomToView(emojiCard)
return view
}()
lazy var emojiCard: UIView = {
let view = UIView()
view.backgroundColor = .white
view.cornerRadius = 16
view.addSubview(emojiImageView)
emojiImageView.layoutChain
.top(5)
.centerX()
.width(30)
.heightToWidth(1)
let hintLab = UILabel()
hintLab.text = "点击发送表情"
hintLab.textColor = UIColor(hexStr: "#9AA1AE")
hintLab.font = .systemFont(ofSize: 12, weight: .medium)
hintLab.textAlignment = .center
view.addSubview(hintLab)
hintLab.layoutChain
.topToBottomOfView(emojiImageView, offset: 6)
.edgesHorzontal(8)
return view
}()
lazy var messageCard: UIView = {
let view = UIView()
view.backgroundColor = .white
view.cornerRadius = 16
let msgView = UIView()
msgView.backgroundColor = UIColor(hexStr: "#F2F2F2")
msgView.cornerRadius = 8
view.addSubview(msgView)
msgView.addSubview(messageTextLab)
msgView.layoutChain
.top(9)
.edgesHorzontal(10)
.height(26)
messageTextLab.layoutChain
.edgesHorzontal(25)
.centerY()
let hintLab = UILabel()
hintLab.text = "点击发送消息"
hintLab.textColor = UIColor(hexStr: "#9AA1AE")
hintLab.font = .systemFont(ofSize: 12, weight: .medium)
hintLab.textAlignment = .center
view.addSubview(hintLab)
hintLab.layoutChain
.topToBottomOfView(msgView, offset: 6)
.edgesHorzontal(8)
return view
}()
lazy var emojiImageView: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFit
return view
}()
lazy var messageTextLab: UILabel = {
let label = UILabel()
label.text = "暂时没有消息记录"
label.textColor = UIColor(hexStr: "#293445")
label.font = .systemFont(ofSize: 12, weight: .medium)
label.textAlignment = .center
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()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
///
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"))
}
}