jsdw_ios/QuickLocation/Section/Map/MemberAnnotationView.swift

167 lines
5.5 KiB
Swift

//
// MemberAnnotationView.swift
// QuickLocation
//
// Created based on Lanhu design: 1..-
//
import Foundation
#if !targetEnvironment(simulator)
import MAMapKit
final class MemberAnnotationView: MAAnnotationView {
// MARK: - Design Constants (50x50 avatar, 1pt border)
static let avatarOuterSize: CGFloat = 50
static let avatarInnerSize: CGFloat = 48
static let nameTagWidth: CGFloat = 60
static let nameTagHeight: CGFloat = 20
static let totalHeight: CGFloat = avatarOuterSize + 4 + nameTagHeight
private let containerView: UIView = {
let v = UIView()
v.backgroundColor = .clear
return v
}()
private let avatarOuterCircle: UIView = {
let v = UIView()
v.backgroundColor = .white
v.layer.cornerRadius = avatarOuterSize / 2
v.layer.shadowColor = UIColor.black.withAlphaComponent(0.15).cgColor
v.layer.shadowOffset = CGSize(width: 0, height: 2)
v.layer.shadowRadius = 4
v.layer.shadowOpacity = 1
return v
}()
private let avatarImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.layer.cornerRadius = avatarInnerSize / 2
iv.layer.borderWidth = 1
iv.layer.borderColor = UIColor.white.cgColor
iv.clipsToBounds = true
iv.backgroundColor = UIColor(hexStr: "#E0E0E0")
return iv
}()
private lazy var nameTagView: UIView = {
let v = UIView()
v.backgroundColor = .white
v.layer.cornerRadius = 4
v.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor
v.layer.shadowOffset = CGSize(width: 0, height: 1)
v.layer.shadowRadius = 2
v.layer.shadowOpacity = 1
return v
}()
private lazy var nameLabel: UILabel = {
let l = UILabel()
l.font = UIFont(name: "PingFangSC-Medium", size: 11)
?? UIFont.systemFont(ofSize: 11, weight: .medium)
l.textColor = UIColor(hexStr: "#0F2846")
l.textAlignment = .center
l.lineBreakMode = .byTruncatingTail
return l
}()
// MARK: - Heading indicator
private var headingLayer: CAShapeLayer?
// MARK: - Init
override init!(annotation: MAAnnotation!, reuseIdentifier: String!) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
canShowCallout = false
isEnabled = true
bounds = CGRect(x: 0, y: 0, width: Self.nameTagWidth, height: Self.totalHeight)
centerOffset = CGPoint(x: 0, y: -Self.totalHeight / 2)
addSubview(containerView)
containerView.frame = bounds
containerView.addSubview(avatarOuterCircle)
avatarOuterCircle.frame = CGRect(
x: (Self.nameTagWidth - Self.avatarOuterSize) / 2, y: 0,
width: Self.avatarOuterSize, height: Self.avatarOuterSize
)
avatarOuterCircle.addSubview(avatarImageView)
avatarImageView.frame = CGRect(
x: (Self.avatarOuterSize - Self.avatarInnerSize) / 2,
y: (Self.avatarOuterSize - Self.avatarInnerSize) / 2,
width: Self.avatarInnerSize, height: Self.avatarInnerSize
)
containerView.addSubview(nameTagView)
nameTagView.frame = CGRect(
x: 0, y: Self.avatarOuterSize + 4,
width: Self.nameTagWidth, height: Self.nameTagHeight
)
nameTagView.addSubview(nameLabel)
nameLabel.frame = nameTagView.bounds.insetBy(dx: 4, dy: 0)
}
// MARK: - Configure
func configure(with member: CircleMember) {
avatarImageView.image = UIImage(named: member.avatar)
avatarImageView.backgroundColor = member.avatar.isEmpty ? UIColor(hexStr: "#E0E0E0") : .clear
nameLabel.text = member.name
if member.isCurrentUser {
nameLabel.textColor = UIColor(hexStr: "#16B3FF")
addHeadingIndicator(heading: member.heading)
} else {
nameLabel.textColor = UIColor(hexStr: "#0F2846")
headingLayer?.removeFromSuperlayer()
headingLayer = nil
}
}
// MARK: - Heading
func updateHeading(_ heading: Double) {
headingLayer?.removeFromSuperlayer()
addHeadingIndicator(heading: heading)
}
/// Draw a 60° semi-transparent blue fan centered on the avatar, pointing in the heading direction.
private func addHeadingIndicator(heading: Double) {
headingLayer?.removeFromSuperlayer()
let center = CGPoint(x: Self.nameTagWidth / 2, y: Self.avatarOuterSize / 2)
let radius: CGFloat = Self.avatarOuterSize / 2 + 16
let halfFan = 30.0 * .pi / 180
let centerAngle = CGFloat(heading) * .pi / 180 - .pi / 2
let startAngle = centerAngle - halfFan
let endAngle = centerAngle + halfFan
let path = UIBezierPath()
path.move(to: center)
path.addArc(withCenter: center, radius: radius,
startAngle: startAngle, endAngle: endAngle,
clockwise: true)
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor(hexStr: "#16B3FF").withAlphaComponent(0.25).cgColor
shapeLayer.strokeColor = UIColor(hexStr: "#16B3FF").withAlphaComponent(0.4).cgColor
shapeLayer.lineWidth = 1
containerView.layer.insertSublayer(shapeLayer, at: 0)
headingLayer = shapeLayer
}
}
#endif