// // QSLHomeFriendView.swift // QuickSearchLocation // // Created by mac on 2024/4/12. // import UIKit enum FriendViewLocation { case ScrollTop case ScrollCenter case ScrollBottom } protocol QSLHomeFriendViewDelegate: NSObjectProtocol { func refreshBtnAction() func homeFriPhoneViewClick() func routeBtnAction(model: QSLUserModel) func locateBtnAction(model: QSLUserModel) func addFriendAction(isSmall: Bool) func viewDidScroll() } class QSLHomeFriendView: UIView { weak var delegate: QSLHomeFriendViewDelegate? var viewModel:QSLHomeViewModel? { didSet { friTableView.reloadData() } } /// 位置 var scrollLocation: FriendViewLocation? /// 滑动到最后停下来的位置 var scrollStopY: CGFloat? /// 滑动顶部距离顶部的距离 var scrollTopHeight: CGFloat? /// 滑动中间距离顶部的距离 var scrollCenterHeight: CGFloat? /// 滑动底部距离顶部的距离 var scrollBottomHeight: CGFloat? lazy var friMapLogoImageView: UIImageView = { let imageView = UIImageView() imageView.isHidden = true imageView.image = UIImage(named: "home_friends_map_logo") return imageView }() lazy var friExpandButton: UIButton = { let button = UIButton(type: .custom) button.backgroundColor = UIColor.hexStringColor(hexString: "#333333", alpha: 0.6) button.addRadius(radius: 2) return button }() lazy var refreshButton: UIButton = { let button = UIButton(type: .custom) button.setBackgroundImage(UIImage(named: "home_refresh_btn"), for: .normal) button.addTarget(self, action: #selector(refreshBtnAction), for: .touchUpInside) return button }() lazy var friBgView: UIView = { let view = UIView() view.clipsToBounds = true view.backgroundColor = QSLColor.backGroundColor view.addRadius(radius: 12.rpx) return view }() lazy var friHeaderView: UIView = { let view = UIView(frame: CGRect(x: 0, y: 0, width: QSLConst.qsl_kScreenW, height: 61.rpx)) if let image = UIImage.gradient([UIColor.hexStringColor(hexString: "#FFFFFF", alpha: 1), UIColor.hexStringColor(hexString: "#FFFFFF", alpha: 0)], size: CGSize(width: qsl_kScreenW, height: 61.rpx), locations: [0, 1], direction: .vertical) { view.backgroundColor = UIColor(patternImage: image) } view.addFourCorner(topLeft: 12.rpx, topRight: 12.rpx, bottomLeft: 0, bottomRight: 0) return view }() lazy var friHeaderTitleIcon: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "home_friends_header_title") return imageView }() lazy var friHeaderAddBtn: UIButton = { let btn = UIButton() btn.setBackgroundImage(UIImage(named: "home_friends_header_add_btn_bg"), for: .normal) btn.image(UIImage(named: "home_friends_header_add_icon")) btn.title("查找好友") btn.mediumFont(15) btn.textColor(.white) btn.setImageTitleLayout(.imgLeft, spacing: 0) btn.addTarget(self, action: #selector(topAddButtonAction), for: .touchUpInside) return btn }() lazy var friTableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.backgroundColor = .clear tableView.separatorStyle = .none tableView.showsVerticalScrollIndicator = false tableView.delegate = self tableView.dataSource = self tableView.tableViewNeverAdjustContentInset() tableView.bounces = false tableView.isUserInteractionEnabled = true tableView.isScrollEnabled = false tableView.contentInsetAdjustmentBehavior = .never tableView.register(cellClass: QSLHomeFriendTableViewCell.self) return tableView }() override init(frame: CGRect) { super.init(frame: frame) self.scrollCenterHeight = self.qsl_top self.scrollBottomHeight = qsl_kScreenH - qsl_kTabbarFrameH - 56 - 40 self.setupUI() self.addPanAction() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc func refreshBtnAction() { delegate?.refreshBtnAction() } } // MARK: - 设置UI extension QSLHomeFriendView { func setupUI() { addSubview(friBgView) addSubview(friTableView) addSubview(friMapLogoImageView) addSubview(friExpandButton) addSubview(refreshButton) addSubview(friHeaderView) friHeaderView.addSubview(friHeaderTitleIcon) friHeaderView.addSubview(friHeaderAddBtn) friMapLogoImageView.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 62, height: 20)) make.top.equalTo(0) make.left.equalTo(12) } friExpandButton.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 80, height: 4)) make.top.equalTo(friMapLogoImageView.snp.bottom).offset(8) make.centerX.equalTo(snp.centerX) } friBgView.snp.makeConstraints { make in make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx) make.left.bottom.right.equalTo(0) } refreshButton.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 40.rpx, height: 40.rpx)) make.right.equalTo(-8.rpx) make.bottom.equalTo(friBgView.snp.top).offset(-12.rpx) } friHeaderView.snp.makeConstraints { make in make.left.right.equalTo(0) make.height.equalTo(61.rpx) make.top.equalTo(friExpandButton.snp.bottom).offset(8.rpx) } friHeaderTitleIcon.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 82.rpx, height: 26.5.rpx)) make.left.equalTo(16.rpx) make.top.equalTo(16.rpx) } friHeaderAddBtn.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 106.rpx, height: 32.rpx)) make.right.equalTo(-8.rpx) make.centerY.equalTo(friHeaderTitleIcon.snp.centerY) } friTableView.snp.makeConstraints { make in make.left.equalTo(0) make.right.equalTo(0) make.top.equalTo(friBgView.snp.top).offset(50.rpx) make.bottom.equalTo(-12.rpx) } } } // MARK: - 点击事件 extension QSLHomeFriendView { @objc func homeFriPhoneViewAction() { delegate?.homeFriPhoneViewClick() } @objc func topAddButtonAction() { delegate?.addFriendAction(isSmall: true) } } extension QSLHomeFriendView: QSLHomeFriendTableViewCellDelegate { func routeBtnAction(model: QSLUserModel) { delegate?.routeBtnAction(model: model) } func locateBtnAction(model: QSLUserModel) { delegate?.locateBtnAction(model: model) } } // MARK: - 添加滑动逻辑 extension QSLHomeFriendView: UIGestureRecognizerDelegate { func addPanAction() { let pan = UIPanGestureRecognizer(target: self, action: #selector(panAction)) pan.delegate = self self.addGestureRecognizer(pan) } @objc func panAction(pan: UIPanGestureRecognizer) { let point = pan.translation(in: self) if let scrollStopY = self.scrollStopY, scrollStopY > 0 { pan.setTranslation(CGPoint(x: 0, y: 0), in: self) return } self.qsl_top += point.y if let scrollTopHeight = self.scrollTopHeight, self.qsl_top < scrollTopHeight { self.qsl_top = scrollTopHeight } if let scrollBottomHeight = self.scrollBottomHeight, self.qsl_top > scrollBottomHeight { self.qsl_top = scrollBottomHeight } if pan.state == .ended || pan.state == .cancelled { let velocity = pan.velocity(in: self) let speed = 350.0 if let scrollLocation = self.scrollLocation { switch scrollLocation { case .ScrollTop: if velocity.y < -speed { self.scrollToLocation(type: .ScrollTop) pan.setTranslation(CGPoint(x: 0, y: 0), in: self) return } else if velocity.y > speed { self.scrollToLocation(type: .ScrollCenter) pan.setTranslation(CGPoint(x: 0, y: 0), in: self) return } break case .ScrollCenter: if velocity.y < -speed { self.scrollToLocation(type: .ScrollTop) pan.setTranslation(CGPoint(x: 0, y: 0), in: self) return } else if velocity.y > speed { self.scrollToLocation(type: .ScrollBottom) pan.setTranslation(CGPoint(x: 0, y: 0), in: self) return } break case .ScrollBottom: if velocity.y < -speed { self.scrollToLocation(type: .ScrollCenter) pan.setTranslation(CGPoint(x: 0, y: 0), in: self) return } else if velocity.y > speed { self.scrollToLocation(type: .ScrollBottom) pan.setTranslation(CGPoint(x: 0, y: 0), in: self) return } break } } if self.qsl_top < (qsl_kScreenH - qsl_kTabbarBottom) / 4 { self.scrollToLocation(type: .ScrollTop) } else if self.qsl_top < (qsl_kScreenH - qsl_kTabbarBottom) / 4 * 3 && self.qsl_top > (qsl_kScreenH - qsl_kTabbarBottom) / 4 { self.scrollToLocation(type: .ScrollCenter) } else { self.scrollToLocation(type: .ScrollBottom) } } pan.setTranslation(CGPoint(x: 0, y: 0), in: self) } func scrollToLocation(type: FriendViewLocation) { let animation = CASpringAnimation(keyPath: "position.y") animation.damping = 10 animation.stiffness = 200 animation.mass = 1 animation.initialVelocity = 10 animation.duration = animation.settlingDuration // animation.fromValue = self.friPhoneView.layer.position.y // animation.toValue = self.friPhoneView.layer.position.y + 2 animation.isRemovedOnCompletion = false animation.fillMode = .forwards let animation1 = CASpringAnimation(keyPath: "position.y") animation1.damping = 10 animation1.stiffness = 200 animation1.mass = 1 animation1.initialVelocity = 10 animation1.duration = animation.settlingDuration animation1.fromValue = self.friTableView.layer.position.y animation1.toValue = self.friTableView.layer.position.y + 2 animation1.isRemovedOnCompletion = false animation1.fillMode = .forwards self.scrollLocation = type if let topHeight = self.scrollTopHeight, let centerHeight = self.scrollCenterHeight, let bottomHeight = self.scrollBottomHeight { switch type { case .ScrollTop: UIView.animate(withDuration: 0.2) { self.qsl_top = topHeight } completion: { finished in self.friTableView.isScrollEnabled = true // self.friPhoneView.layer.add(animation, forKey: "animation") self.friTableView.layer.add(animation1, forKey: "animation") } break case .ScrollCenter: UIView.animate(withDuration: 0.2) { self.qsl_top = centerHeight } completion: { finished in self.friTableView.isScrollEnabled = false // self.friPhoneView.layer.add(animation, forKey: "animation") self.friTableView.layer.add(animation1, forKey: "animation") } break case .ScrollBottom: UIView.animate(withDuration: 0.2) { self.qsl_top = bottomHeight } completion: { finished in self.friTableView.isScrollEnabled = false // self.friPhoneView.layer.add(animation, forKey: "animation") self.friTableView.layer.add(animation1, forKey: "animation") } break } } delegate?.viewDidScroll() } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } func scrollViewDidScroll(_ scrollView: UIScrollView) { let currentPostion = scrollView.contentOffset.y self.scrollStopY = currentPostion } } extension QSLHomeFriendView: QSLHomeFriendFooterViewDelegate { func addButtonAction() { delegate?.addFriendAction(isSmall: false) } } // MARK: - 设置Tableview extension QSLHomeFriendView: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { guard let friendList = viewModel?.friendList else { return 0 } return friendList.count } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { // if indexPath.row == 0 { // // cell.addCorner(conrners: [.topRight, .topLeft], radius: 6) // } // // if let friendList = viewModel?.friendList, indexPath.row == friendList.count - 1 { // // cell.addCorner(conrners: [.bottomLeft, .bottomRight], radius: 6) // } } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(cellType: QSLHomeFriendTableViewCell.self, cellForRowAt: indexPath) cell.selectionStyle = .none cell.delegate = self if let model = viewModel?.friendList[indexPath.row] { cell.config(model: model) } return cell } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 94.rpx } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 0.0001 } func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return 200.rpx } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { let view = QSLHomeFriendFooterView() view.delegate = self return view } }