708 lines
24 KiB
Swift
708 lines
24 KiB
Swift
//
|
||
// CreateSchedulePopView.swift
|
||
// QuickLocation
|
||
//
|
||
// Created by 八条 on 2026/6/23.
|
||
//
|
||
|
||
import UIKit
|
||
import RxSwift
|
||
import RxCocoa
|
||
import RxDataSources
|
||
import BRPickerView
|
||
import RxGesture
|
||
import CoreLocation
|
||
import TagListView
|
||
import SwiftDate
|
||
|
||
// MARK: - CreateSchedulePopView
|
||
class CreateSchedulePopView: UIView {
|
||
|
||
var disposeBag = DisposeBag()
|
||
/// 途经点拖拽排序中,外层 Pop pan 应让路
|
||
var isReorderingPoints = false
|
||
|
||
func setupTagData(_ list: [GroupInfoModel]) {
|
||
let nameArr = list.map { $0.name }
|
||
tagListView.removeAllTags()
|
||
tagListView.addTags(nameArr)
|
||
tagListView.tagViews.forEach {
|
||
$0.layer.cornerRadius = 4
|
||
$0.layer.borderWidth = 1
|
||
$0.layer.borderColor = UIColor.clear.cgColor
|
||
}
|
||
tagListView.setNeedsLayout()
|
||
tagListView.layoutIfNeeded()
|
||
}
|
||
|
||
// MARK: - UI
|
||
private func setupUI() {
|
||
addSubview(lineView)
|
||
addSubview(headerView)
|
||
addSubview(scrollView)
|
||
|
||
lineView.layoutChain
|
||
.top(15)
|
||
.centerX()
|
||
.width(36)
|
||
.height(4)
|
||
|
||
headerView.layoutChain
|
||
.topToBottomOfView(lineView, offset: 16)
|
||
.edgesHorzontal()
|
||
.height(24)
|
||
|
||
scrollView.layoutChain
|
||
.topToBottomOfView(headerView, offset: 16)
|
||
.edges(excludingEdge: .top)
|
||
}
|
||
|
||
// MARK: - Views
|
||
|
||
lazy var lineView: UIView = {
|
||
let v = UIView()
|
||
v.backgroundColor = UIColor(hexStr: "#EBEBEB")
|
||
v.cornerRadius = 2
|
||
return v
|
||
}()
|
||
|
||
lazy var headerView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
|
||
let pin = UIImageView(image: UIImage(named: "Schedule/location")?.withRenderingMode(.alwaysTemplate))
|
||
pin.tintColor = UIColor(hexStr: "#16B3FF")
|
||
pin.contentMode = .scaleAspectFit
|
||
view.addSubview(pin)
|
||
pin.layoutChain.left(16).centerY().width(14).height(14)
|
||
|
||
let titleLab = UILabel()
|
||
titleLab.text = "行程详情"
|
||
titleLab.font = .systemFont(ofSize: 16, weight: .bold)
|
||
titleLab.textColor = UIColor(hexStr: "#293445")
|
||
view.addSubview(titleLab)
|
||
titleLab.layoutChain.leftToRightOfView(pin, offset: 6).centerY()
|
||
|
||
let icon = UIImageView(image: UIImage(named: "Common/arrow_right"))
|
||
view.addSubview(icon)
|
||
|
||
icon.layoutChain
|
||
.right(15)
|
||
.centerY()
|
||
.width(10)
|
||
.height(10)
|
||
|
||
view.addSubview(dateLab)
|
||
dateLab.layoutChain.rightToLeftOfView(icon, offset: -6).centerY()
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var dateLab: UILabel = {
|
||
let label = UILabel()
|
||
label.font = .systemFont(ofSize: 13, weight: .medium)
|
||
label.textColor = UIColor(hexStr: "#293445")
|
||
label.isUserInteractionEnabled = true
|
||
label.text = Date().toFormat("yyyy年MM月dd日")
|
||
return label
|
||
}()
|
||
|
||
/// 兼容旧绑定:标题旁 + 已移除,保留空按钮避免崩溃
|
||
lazy var addBtn: UIButton = {
|
||
let btn = UIButton(type: .custom)
|
||
btn.isHidden = true
|
||
return btn
|
||
}()
|
||
|
||
lazy var scrollView: UIScrollView = {
|
||
let view = UIScrollView()
|
||
view.backgroundColor = .clear
|
||
view.showsHorizontalScrollIndicator = false
|
||
view.bounces = false
|
||
view.delaysContentTouches = false
|
||
|
||
let contentView = UIView()
|
||
contentView.backgroundColor = .clear
|
||
view.addSubview(contentView)
|
||
contentView.layoutChain
|
||
.edges().widthToView(view)
|
||
|
||
contentView.addSubview(tableView)
|
||
tableView.layoutChain
|
||
.top()
|
||
.edgesHorzontal()
|
||
|
||
contentView.addSubview(shareGroupView)
|
||
shareGroupView.layoutChain
|
||
.topToBottomOfView(tableView, offset: 8)
|
||
.edgesHorzontal()
|
||
|
||
contentView.addSubview(createBtn)
|
||
createBtn.layoutChain
|
||
.topToBottomOfView(shareGroupView, offset: 24)
|
||
.edgesHorzontal(16)
|
||
.height(50)
|
||
.bottom(kSafeBottomMargin)
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var tableView: UITableView = {
|
||
let tv = UITableView(frame: .zero, style: .plain)
|
||
tv.backgroundColor = .clear
|
||
tv.separatorStyle = .none
|
||
tv.estimatedRowHeight = ScheduleTimelineMetric.pointRowHeight
|
||
tv.rowHeight = UITableView.automaticDimension
|
||
tv.isScrollEnabled = false
|
||
tv.showsVerticalScrollIndicator = false
|
||
tv.bounces = false
|
||
tv.clipsToBounds = false
|
||
tv.register(SchedulePointCell.self)
|
||
tv.register(ScheduleAddPointCell.self)
|
||
return tv
|
||
}()
|
||
|
||
lazy var shareGroupView: UIView = {
|
||
let view = UIView()
|
||
view.backgroundColor = .clear
|
||
|
||
let pin = UIImageView(image: UIImage(named: "Schedule/location")?.withRenderingMode(.alwaysTemplate))
|
||
pin.tintColor = UIColor(hexStr: "#16B3FF")
|
||
pin.contentMode = .scaleAspectFit
|
||
view.addSubview(pin)
|
||
pin.layoutChain.left(16).top().width(14).height(14)
|
||
|
||
let sectionLab = UILabel()
|
||
sectionLab.text = "其他"
|
||
sectionLab.font = .systemFont(ofSize: 16, weight: .bold)
|
||
sectionLab.textColor = UIColor(hexStr: "#293445")
|
||
view.addSubview(sectionLab)
|
||
sectionLab.layoutChain.leftToRightOfView(pin, offset: 6).centerY(pin)
|
||
|
||
let card = UIView()
|
||
card.backgroundColor = .white
|
||
card.layer.cornerRadius = 12
|
||
view.addSubview(card)
|
||
card.layoutChain
|
||
.topToBottomOfView(sectionLab, offset: 12)
|
||
.edgesHorzontal(16)
|
||
.bottom()
|
||
|
||
let titleLab = UILabel()
|
||
titleLab.text = "分享的圈子"
|
||
titleLab.font = .systemFont(ofSize: 14, weight: .medium)
|
||
titleLab.textColor = UIColor(hexStr: "#1A1A1A")
|
||
card.addSubview(titleLab)
|
||
titleLab.layoutChain.left(16).top(19)
|
||
|
||
card.addSubview(tagListView)
|
||
tagListView.layoutChain
|
||
.topToBottomOfView(titleLab, offset: 12)
|
||
.edgesHorzontal(16)
|
||
.bottom(12)
|
||
|
||
return view
|
||
}()
|
||
|
||
lazy var tagListView: TagListView = {
|
||
let view = TagListView()
|
||
view.textFont = UIFont.systemFont(ofSize: 12, weight: .medium)
|
||
view.textColor = UIColor(hexStr: "#AAAAAA")
|
||
view.tagBackgroundColor = UIColor(hexStr: "#F0F0F0")
|
||
view.tagBorderColor = UIColor.clear
|
||
view.selectedTextColor = UIColor(hexStr: "#00ADFE")
|
||
view.tagSelectedBackgroundColor = UIColor(hexStr: "#E3F6FF")
|
||
view.selectedBorderColor = UIColor(hexStr: "#16B3FF")
|
||
view.paddingX = 15
|
||
view.paddingY = 10
|
||
view.alignment = .left
|
||
view.translatesAutoresizingMaskIntoConstraints = false
|
||
return view
|
||
}()
|
||
|
||
lazy var createBtn: UIButton = {
|
||
let btn = UIButton(type: .custom)
|
||
btn.setTitle("发布行程", for: .normal)
|
||
btn.setTitleColor(.white, for: .normal)
|
||
btn.titleLabel?.font = FontManager.boboBold(18)
|
||
btn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
|
||
btn.cornerRadius = 16
|
||
return btn
|
||
}()
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
backgroundColor = UIColor(hexStr: "#FAFAFA")
|
||
layer.cornerRadius = 30
|
||
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||
layer.masksToBounds = true
|
||
setupUI()
|
||
}
|
||
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
}
|
||
|
||
// MARK: - Timeline metrics
|
||
|
||
enum ScheduleTimelineMetric {
|
||
/// marker left 16 + width 22 / 2
|
||
static let centerX: CGFloat = 27
|
||
static let markerSize: CGFloat = 18
|
||
/// 8 + 10+36+8+36+8+36+10 + 8,需与卡片内部约束严格一致,否则会冲突
|
||
static let pointRowHeight: CGFloat = 160
|
||
static let addRowHeight: CGFloat = 60
|
||
}
|
||
|
||
// MARK: - SchedulePointCell
|
||
|
||
final class SchedulePointCell: UITableViewCell {
|
||
|
||
static let reuseId = "SchedulePointCell"
|
||
|
||
var disposeBag = DisposeBag()
|
||
|
||
var onLocationTap: (() -> Void)?
|
||
var onTimeTap: (() -> Void)?
|
||
var onDelete: (() -> Void)?
|
||
/// 拖拽手柄长按手势(began/changed/ended),由 VC 处理跟手排序
|
||
var onDragGesture: ((UILongPressGestureRecognizer) -> Void)?
|
||
|
||
private let dashLineView = ScheduleDashLineView()
|
||
private let markerView = UIView()
|
||
private let markerLab = UILabel()
|
||
private let middleDot = UIView()
|
||
|
||
private let cardView = UIView()
|
||
private let locationRow = ScheduleFieldRowView(
|
||
icon: UIImage(named: "Schedule/location"),
|
||
placeholder: "请选择"
|
||
)
|
||
private let timeRow = ScheduleFieldRowView(
|
||
icon: UIImage(named: "Schedule/clock"),
|
||
placeholder: "请选择"
|
||
)
|
||
private let remarkRow = ScheduleFieldRowView(
|
||
icon: UIImage(named: "Schedule/edit"),
|
||
placeholder: "在此添加备注",
|
||
editable: true
|
||
)
|
||
|
||
private let dragBtn = UIButton(type: .custom)
|
||
private let deleteBtn = UIButton(type: .custom)
|
||
private var locationRowRightConstraint: NSLayoutConstraint?
|
||
private var remarkRowRightConstraint: NSLayoutConstraint?
|
||
private var dragPress: UILongPressGestureRecognizer?
|
||
private var dashTopToContent: NSLayoutConstraint?
|
||
private var dashTopToMarkerCenter: NSLayoutConstraint?
|
||
private var dashBottomToContent: NSLayoutConstraint?
|
||
private var dashBottomToMarkerCenter: NSLayoutConstraint?
|
||
|
||
func configure(item: SchedulePointItem, role: SchedulePointRole) {
|
||
switch role {
|
||
case .start:
|
||
markerView.isHidden = false
|
||
middleDot.isHidden = true
|
||
markerView.backgroundColor = UIColor(hexStr: "#16B3FF")
|
||
markerLab.text = "始"
|
||
markerLab.isHidden = false
|
||
dragBtn.isHidden = true
|
||
deleteBtn.isHidden = true
|
||
locationRowRightConstraint?.constant = -10
|
||
remarkRowRightConstraint?.constant = -10
|
||
// 虚线从始圆点开始往下
|
||
dashTopToContent?.isActive = false
|
||
dashTopToMarkerCenter?.isActive = true
|
||
dashBottomToMarkerCenter?.isActive = false
|
||
dashBottomToContent?.isActive = true
|
||
case .middle:
|
||
markerView.isHidden = true
|
||
middleDot.isHidden = false
|
||
markerLab.isHidden = true
|
||
dragBtn.isHidden = false
|
||
deleteBtn.isHidden = false
|
||
locationRowRightConstraint?.constant = -40
|
||
remarkRowRightConstraint?.constant = -40
|
||
// 虚线贯穿整行
|
||
dashTopToMarkerCenter?.isActive = false
|
||
dashTopToContent?.isActive = true
|
||
dashBottomToMarkerCenter?.isActive = false
|
||
dashBottomToContent?.isActive = true
|
||
case .end:
|
||
markerView.isHidden = false
|
||
middleDot.isHidden = true
|
||
markerView.backgroundColor = UIColor(hexStr: "#FF5A5A")
|
||
markerLab.text = "终"
|
||
markerLab.isHidden = false
|
||
dragBtn.isHidden = true
|
||
deleteBtn.isHidden = true
|
||
locationRowRightConstraint?.constant = -10
|
||
remarkRowRightConstraint?.constant = -10
|
||
// 虚线到终圆点结束
|
||
dashTopToMarkerCenter?.isActive = false
|
||
dashTopToContent?.isActive = true
|
||
dashBottomToContent?.isActive = false
|
||
dashBottomToMarkerCenter?.isActive = true
|
||
}
|
||
|
||
let locationText = item.street.isEmpty ? (item.locationName.isEmpty ? nil : item.locationName) : item.street
|
||
locationRow.setText(locationText)
|
||
if let expectedTime = item.expectedTime {
|
||
timeRow.setText(expectedTime.toFormat("HH:mm"))
|
||
} else {
|
||
timeRow.setText(nil)
|
||
}
|
||
remarkRow.setText(item.remark.isEmpty ? nil : item.remark)
|
||
remarkRow.textField?.text = item.remark
|
||
|
||
locationRow.onTap = { [weak self] in self?.onLocationTap?() }
|
||
timeRow.onTap = { [weak self] in self?.onTimeTap?() }
|
||
}
|
||
|
||
var remarkText: String {
|
||
remarkRow.textField?.text ?? ""
|
||
}
|
||
|
||
var remarkTextField: UITextField? { remarkRow.textField }
|
||
|
||
override init(style: CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
selectionStyle = .none
|
||
backgroundColor = .clear
|
||
contentView.backgroundColor = .clear
|
||
clipsToBounds = false
|
||
contentView.clipsToBounds = false
|
||
setupViews()
|
||
}
|
||
|
||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
override func prepareForReuse() {
|
||
super.prepareForReuse()
|
||
disposeBag = DisposeBag()
|
||
onLocationTap = nil
|
||
onTimeTap = nil
|
||
onDelete = nil
|
||
onDragGesture = nil
|
||
}
|
||
|
||
private func setupViews() {
|
||
// 先加虚线,再加圆点盖在上面
|
||
contentView.addSubview(dashLineView)
|
||
contentView.addSubview(markerView)
|
||
contentView.addSubview(middleDot)
|
||
contentView.addSubview(cardView)
|
||
markerView.addSubview(markerLab)
|
||
|
||
cardView.addSubview(locationRow)
|
||
cardView.addSubview(timeRow)
|
||
cardView.addSubview(remarkRow)
|
||
cardView.addSubview(dragBtn)
|
||
cardView.addSubview(deleteBtn)
|
||
|
||
markerView.layer.cornerRadius = ScheduleTimelineMetric.markerSize / 2
|
||
markerView.clipsToBounds = true
|
||
markerLab.textColor = .white
|
||
markerLab.font = FontManager.youSheBiaoTiHei(11)
|
||
markerLab.textAlignment = .center
|
||
|
||
middleDot.backgroundColor = UIColor(hexStr: "#293445")
|
||
middleDot.layer.cornerRadius = 5
|
||
let middleCenter = UIView()
|
||
middleCenter.backgroundColor = .white
|
||
middleCenter.cornerRadius = 2
|
||
middleDot.addSubview(middleCenter)
|
||
middleCenter.layoutChain
|
||
.centerX()
|
||
.centerY()
|
||
.width(4)
|
||
.heightToWidth(1)
|
||
|
||
cardView.backgroundColor = .white
|
||
cardView.layer.cornerRadius = 16
|
||
|
||
dragBtn.setImage(UIImage(named: "Schedule/drag")?.withRenderingMode(.alwaysOriginal), for: .normal)
|
||
dragBtn.extendEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
|
||
|
||
deleteBtn.setImage(UIImage(named: "Schedule/delete")?.withRenderingMode(.alwaysOriginal), for: .normal)
|
||
deleteBtn.extendEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||
deleteBtn.addTarget(self, action: #selector(tapDelete), for: .touchUpInside)
|
||
|
||
let press = UILongPressGestureRecognizer(target: self, action: #selector(handleDragPress(_:)))
|
||
press.minimumPressDuration = 0.12
|
||
dragBtn.addGestureRecognizer(press)
|
||
dragPress = press
|
||
|
||
dashLineView.translatesAutoresizingMaskIntoConstraints = false
|
||
markerView.translatesAutoresizingMaskIntoConstraints = false
|
||
|
||
let topToContent = dashLineView.topAnchor.constraint(equalTo: contentView.topAnchor)
|
||
let topToMarker = dashLineView.topAnchor.constraint(equalTo: markerView.centerYAnchor)
|
||
let bottomToContent = dashLineView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
|
||
let bottomToMarker = dashLineView.bottomAnchor.constraint(equalTo: markerView.centerYAnchor)
|
||
dashTopToContent = topToContent
|
||
dashTopToMarkerCenter = topToMarker
|
||
dashBottomToContent = bottomToContent
|
||
dashBottomToMarkerCenter = bottomToMarker
|
||
// 默认中间点:贯穿;configure 时再按 role 切换
|
||
topToMarker.isActive = false
|
||
bottomToMarker.isActive = false
|
||
|
||
NSLayoutConstraint.activate([
|
||
topToContent,
|
||
bottomToContent,
|
||
dashLineView.centerXAnchor.constraint(equalTo: contentView.leftAnchor, constant: ScheduleTimelineMetric.centerX),
|
||
dashLineView.widthAnchor.constraint(equalToConstant: 1),
|
||
|
||
markerView.centerXAnchor.constraint(equalTo: contentView.leftAnchor, constant: ScheduleTimelineMetric.centerX),
|
||
markerView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
||
markerView.widthAnchor.constraint(equalToConstant: ScheduleTimelineMetric.markerSize),
|
||
markerView.heightAnchor.constraint(equalToConstant: ScheduleTimelineMetric.markerSize)
|
||
])
|
||
|
||
markerLab.layoutChain.edges()
|
||
|
||
middleDot.layoutChain
|
||
.centerX(markerView)
|
||
.centerY(markerView)
|
||
.width(10)
|
||
.height(10)
|
||
|
||
cardView.layoutChain
|
||
.top(8)
|
||
.left(50)
|
||
.right(16)
|
||
.bottom(8)
|
||
|
||
// 上:时间,中:地点,下:备注
|
||
timeRow.layoutChain
|
||
.top(10)
|
||
.left(10)
|
||
.right(10)
|
||
.height(36)
|
||
|
||
locationRow.translatesAutoresizingMaskIntoConstraints = false
|
||
remarkRow.translatesAutoresizingMaskIntoConstraints = false
|
||
NSLayoutConstraint.activate([
|
||
locationRow.topAnchor.constraint(equalTo: timeRow.bottomAnchor, constant: 8),
|
||
locationRow.leftAnchor.constraint(equalTo: cardView.leftAnchor, constant: 10),
|
||
locationRow.heightAnchor.constraint(equalToConstant: 36),
|
||
remarkRow.topAnchor.constraint(equalTo: locationRow.bottomAnchor, constant: 8),
|
||
remarkRow.leftAnchor.constraint(equalTo: cardView.leftAnchor, constant: 10),
|
||
remarkRow.heightAnchor.constraint(equalToConstant: 36),
|
||
remarkRow.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: -10)
|
||
])
|
||
let locationRight = locationRow.rightAnchor.constraint(equalTo: cardView.rightAnchor, constant: -10)
|
||
let remarkRight = remarkRow.rightAnchor.constraint(equalTo: cardView.rightAnchor, constant: -10)
|
||
locationRight.isActive = true
|
||
remarkRight.isActive = true
|
||
locationRowRightConstraint = locationRight
|
||
remarkRowRightConstraint = remarkRight
|
||
|
||
dragBtn.layoutChain
|
||
.right(10)
|
||
.centerY(locationRow)
|
||
.width(22)
|
||
.height(22)
|
||
|
||
deleteBtn.layoutChain
|
||
.right(12)
|
||
.centerY(remarkRow)
|
||
.width(18)
|
||
.height(18)
|
||
}
|
||
|
||
@objc private func tapDelete() {
|
||
onDelete?()
|
||
}
|
||
|
||
@objc private func handleDragPress(_ gesture: UILongPressGestureRecognizer) {
|
||
onDragGesture?(gesture)
|
||
}
|
||
}
|
||
|
||
// MARK: - ScheduleAddPointCell
|
||
|
||
final class ScheduleAddPointCell: UITableViewCell {
|
||
static let reuseId = "ScheduleAddPointCell"
|
||
|
||
var onAdd: (() -> Void)?
|
||
|
||
private let dashLineView = ScheduleDashLineView()
|
||
private let addBtn = UIButton(type: .custom)
|
||
|
||
override init(style: CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
selectionStyle = .none
|
||
backgroundColor = .clear
|
||
contentView.backgroundColor = .clear
|
||
clipsToBounds = false
|
||
contentView.clipsToBounds = false
|
||
setup()
|
||
}
|
||
|
||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
private func setup() {
|
||
contentView.addSubview(dashLineView)
|
||
contentView.addSubview(addBtn)
|
||
|
||
addBtn.setTitle(" 添加行程", for: .normal)
|
||
addBtn.setTitleColor(UIColor(hexStr: "#16B3FF"), for: .normal)
|
||
addBtn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
|
||
addBtn.setImage(UIImage(named: "Schedule/add")?.withRenderingMode(.alwaysOriginal), for: .normal)
|
||
addBtn.backgroundColor = UIColor(hexStr: "#F6FBFF")
|
||
addBtn.layer.cornerRadius = 12
|
||
addBtn.addTarget(self, action: #selector(tapAdd), for: .touchUpInside)
|
||
|
||
dashLineView.translatesAutoresizingMaskIntoConstraints = false
|
||
NSLayoutConstraint.activate([
|
||
dashLineView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||
dashLineView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||
dashLineView.centerXAnchor.constraint(equalTo: contentView.leftAnchor, constant: ScheduleTimelineMetric.centerX),
|
||
dashLineView.widthAnchor.constraint(equalToConstant: 1)
|
||
])
|
||
|
||
addBtn.layoutChain
|
||
.top(8)
|
||
.left(50)
|
||
.right(16)
|
||
.height(44)
|
||
.bottom(8)
|
||
}
|
||
|
||
private var dashBorder: CAShapeLayer?
|
||
|
||
override func layoutSubviews() {
|
||
super.layoutSubviews()
|
||
dashBorder?.removeFromSuperlayer()
|
||
let border = CAShapeLayer()
|
||
border.strokeColor = UIColor(hexStr: "#16B3FF").cgColor
|
||
border.fillColor = nil
|
||
border.lineDashPattern = [5, 4]
|
||
border.lineWidth = 1
|
||
border.frame = addBtn.bounds
|
||
border.path = UIBezierPath(roundedRect: addBtn.bounds, cornerRadius: 12).cgPath
|
||
addBtn.layer.borderWidth = 0
|
||
addBtn.layer.addSublayer(border)
|
||
dashBorder = border
|
||
}
|
||
|
||
@objc private func tapAdd() {
|
||
onAdd?()
|
||
}
|
||
}
|
||
|
||
// MARK: - Field row
|
||
|
||
private final class ScheduleFieldRowView: UIView {
|
||
var onTap: (() -> Void)?
|
||
private(set) var textField: UITextField?
|
||
|
||
private let iconView = UIImageView()
|
||
private let label = UILabel()
|
||
private let placeholder: String
|
||
|
||
init(icon: UIImage?, placeholder: String, editable: Bool = false) {
|
||
self.placeholder = placeholder
|
||
super.init(frame: .zero)
|
||
backgroundColor = .white
|
||
layer.cornerRadius = 8
|
||
|
||
iconView.image = icon?.withRenderingMode(.alwaysOriginal)
|
||
iconView.contentMode = .scaleAspectFit
|
||
addSubview(iconView)
|
||
iconView.layoutChain.left(10).centerY().width(14).height(14)
|
||
|
||
let tfView = UIView()
|
||
tfView.backgroundColor = UIColor(hexStr: "#FAFAFA")
|
||
tfView.cornerRadius = 8
|
||
addSubview(tfView)
|
||
tfView.layoutChain.leftToRightOfView(iconView, offset: 8).right(10).centerY().height(34)
|
||
|
||
if editable {
|
||
let tf = UITextField()
|
||
tf.font = .systemFont(ofSize: 13, weight: .medium)
|
||
tf.textColor = UIColor(hexStr: "#333333")
|
||
tf.placeholder = placeholder
|
||
tf.returnKeyType = .done
|
||
tfView.addSubview(tf)
|
||
tf.layoutChain.edgesHorzontal(10).edgesVertical()
|
||
textField = tf
|
||
} else {
|
||
label.font = .systemFont(ofSize: 13, weight: .medium)
|
||
label.textColor = UIColor(hexStr: "#999999")
|
||
label.text = placeholder
|
||
label.isUserInteractionEnabled = true
|
||
tfView.addSubview(label)
|
||
label.layoutChain.edgesHorzontal(10).centerY()
|
||
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
|
||
addGestureRecognizer(tap)
|
||
}
|
||
}
|
||
|
||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
func setText(_ text: String?) {
|
||
if let tf = textField {
|
||
tf.text = text
|
||
return
|
||
}
|
||
if let text, !text.isEmpty {
|
||
label.text = text
|
||
label.textColor = UIColor(hexStr: "#333333")
|
||
} else {
|
||
label.text = placeholder
|
||
label.textColor = UIColor(hexStr: "#999999")
|
||
}
|
||
}
|
||
|
||
@objc private func handleTap() {
|
||
onTap?()
|
||
}
|
||
}
|
||
|
||
// MARK: - Dash line
|
||
|
||
final class ScheduleDashLineView: UIView {
|
||
override func layoutSubviews() {
|
||
super.layoutSubviews()
|
||
layer.sublayers?.forEach { if $0 is CAShapeLayer { $0.removeFromSuperlayer() } }
|
||
|
||
let shapeLayer = CAShapeLayer()
|
||
shapeLayer.strokeColor = UIColor(hexStr: "#E0E0E0").cgColor
|
||
shapeLayer.lineWidth = 1
|
||
shapeLayer.lineDashPattern = [4, 4]
|
||
shapeLayer.fillColor = nil
|
||
|
||
let path = UIBezierPath()
|
||
path.move(to: CGPoint(x: bounds.midX, y: 0))
|
||
path.addLine(to: CGPoint(x: bounds.maxX == 0 ? 0.5 : bounds.midX, y: bounds.height))
|
||
shapeLayer.path = path.cgPath
|
||
layer.addSublayer(shapeLayer)
|
||
}
|
||
}
|
||
|
||
/// 兼容旧命名
|
||
typealias DashLineView = ScheduleDashLineView
|
||
|
||
final class HorizontalDashLineView: UIView {
|
||
override func layoutSubviews() {
|
||
super.layoutSubviews()
|
||
layer.sublayers?.forEach { if $0 is CAShapeLayer { $0.removeFromSuperlayer() } }
|
||
|
||
let shapeLayer = CAShapeLayer()
|
||
shapeLayer.strokeColor = UIColor(hexStr: "#E0E0E0").cgColor
|
||
shapeLayer.lineWidth = 1
|
||
shapeLayer.lineDashPattern = [4, 4]
|
||
shapeLayer.fillColor = nil
|
||
|
||
let path = UIBezierPath()
|
||
path.move(to: CGPoint(x: 0, y: bounds.midY))
|
||
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.midY))
|
||
shapeLayer.path = path.cgPath
|
||
layer.addSublayer(shapeLayer)
|
||
}
|
||
}
|