| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- //
- // MOLineUserListView.swift
- // MiMoLive
- //
- // Created by OneeChan on 2025/9/23.
- //
- import Foundation
- import UIKit
- protocol MOLineUserListViewDelegate: AnyObject {
- func onUserListViewClickSearch(view: MOLineUserListView, type: LineType)
- func onUserListViewClickMatch(view: MOLineUserListView, type: LineType)
- }
- class MOLineUserListView: UIView {
- private let curType: LineType
- private var userList: [MOLivePkLinkInviteVO] = []
- private var nextTag = ""
-
- private let userTitle = UILabel()
- private let noMoreDataView = MONoMoreDataView()
- private let tableView = UITableView()
-
- private weak var delegate: MOLineUserListViewDelegate?
-
- init(type: LineType, _ delegate: MOLineUserListViewDelegate?) {
- self.curType = type
- self.delegate = delegate
- super.init(frame: .zero)
-
- setupViews()
-
- reload()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- extension MOLineUserListView {
- private func reload() {
- MOLineViewModel.loadLineMatchUsers { [weak self] list, hasMore in
- guard let self else { return }
- self.tableView.mj_header?.endRefreshing()
-
- guard let list else { return }
-
- self.noMoreDataView.isHaveData = !list.list.isEmpty
- self.userList = list.list
- self.nextTag = list.next
-
- if !hasMore {
- self.tableView.mj_footer?.endRefreshingWithNoMoreData()
- }
- self.tableView.reloadData()
- self.updateUserCountText()
- }
- }
-
- @objc
- private func loadNext() {
- guard !self.nextTag.isEmpty else {
- self.tableView.mj_footer?.endRefreshing()
- return
- }
- MOLineViewModel.loadLineMatchUsers(self.nextTag) { [weak self] list, hasMore in
- guard let self else { return }
- self.tableView.mj_footer?.endRefreshing()
-
- guard let list else { return }
- self.userList.append(contentsOf: list.list)
- self.nextTag = list.next
-
- if !hasMore {
- self.tableView.mj_footer?.endRefreshingWithNoMoreData()
- }
- self.tableView.reloadData()
- self.updateUserCountText()
- }
- }
- }
- extension MOLineUserListView {
- @objc
- private func handleSearchClick() {
- delegate?.onUserListViewClickSearch(view: self, type: curType)
- }
-
- @objc
- private func handleMatchClick() {
- delegate?.onUserListViewClickMatch(view: self, type: curType)
- }
- }
- extension MOLineUserListView: JXCategoryListContentViewDelegate {
- func listView() -> UIView! {
- self
- }
- }
- extension MOLineUserListView: UITableViewDataSource {
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- userList.count
- }
-
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let cell = tableView.dequeueReusableCell(withIdentifier: MOLineUserListCell.className())
-
- if indexPath.row < userList.count, let cell = cell as? MOLineUserListCell {
- cell.updateContent(MOLineUserListItemData(curType: curType, item: userList[indexPath.row]))
- }
-
- return cell!
- }
- }
- extension MOLineUserListView: UITableViewDelegate {
- func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
- 64
- }
- }
- extension MOLineUserListView {
- private func updateUserCountText() {
- let text: String = curType == .line ? .init(key: "C70054", userList.count) : .init(key: "C70062", userList.count)
- let attrStr = NSMutableAttributedString(string: text)
- let range = (attrStr.string as NSString).range(of: "\(userList.count)")
- attrStr.addAttributes([.font: UIFont.systemFont(ofSize: 16, weight: .semibold),
- .foregroundColor: UIColor.init(hex: "#4363FF")], range: range)
- userTitle.attributedText = attrStr
- }
-
- private func setupViews() {
- let search = buildSearch()
- addSubview(search)
- search.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.trailing.equalToSuperview().offset(-12)
- make.top.equalToSuperview().offset(16)
- make.height.equalTo(36)
- }
-
- let match = curType == .line ? buildLineMatch() : buildPkMatch()
- addSubview(match)
- match.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.trailing.equalToSuperview().offset(-12)
- make.top.equalTo(search.snp.bottom).offset(15)
- }
-
- let list = buildList()
- addSubview(list)
- list.snp.makeConstraints { make in
- make.leading.equalToSuperview()
- make.trailing.equalToSuperview()
- make.bottom.equalToSuperview()
- make.top.equalTo(match.snp.bottom).offset(19)
- }
- }
-
- private func buildSearch() -> UIView {
- let container = UIView()
- container.layer.cornerRadius = 8
- container.backgroundColor = .init(hex: "#F3F4FA")
-
- let search = UIImageView()
- search.image = UIImage(named: "icon_search")
- search.setContentHuggingPriority(.defaultHigh, for: .horizontal)
- container.addSubview(search)
- search.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.centerY.equalToSuperview()
- }
-
- let hint = UILabel()
- hint.text = .init(key: "mimo_contact_search_hint")
- hint.font = .poppinsRegularFont(14)
- hint.textColor = .init(hex: "#878A99", alpha: 0.5)
- container.addSubview(hint)
- hint.snp.makeConstraints { make in
- make.leading.equalTo(search.snp.trailing).offset(6)
- make.trailing.equalToSuperview().offset(-6)
- make.centerY.equalToSuperview()
- }
-
- let button = UIButton()
- button.addTarget(self, action: #selector(handleSearchClick), for: .touchUpInside)
- container.addSubview(button)
- button.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- return container
- }
-
- private func buildLineMatch() -> UIView {
- let container = UIView()
-
- let bg = UIImageView()
- bg.image = UIImage(named: "icon_line_match_bg")
- container.addSubview(bg)
- bg.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- let match = UIImageView()
- let path = Bundle.main.path(forResource: "icon_line_match", ofType: "webp")
- if let path {
- match.sd_setImage(with: URL(fileURLWithPath: path))
- } else {
- match.isHidden = true
- }
- container.addSubview(match)
- match.snp.makeConstraints { make in
- make.top.equalToSuperview().offset(4)
- make.trailing.equalToSuperview().offset(-4)
- make.width.equalTo(90)
- make.height.equalTo(80)
- }
-
- let title = UILabel()
- title.text = .init(key: "C70051")
- title.font = .poppinsBoldFont(20)
- title.textColor = .init(hex: "#141924")
- container.addSubview(title)
- title.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(13)
- make.top.equalToSuperview().offset(13)
- }
-
- let text: String = .init(key: "C70052", 15)
- let attrStr = NSMutableAttributedString(string: text)
- let range = (attrStr.string as NSString).range(of: "15s")
- attrStr.addAttributes([.foregroundColor: UIColor.init(hex: "#4363FF")], range: range)
- let desc = UILabel()
- desc.font = .poppinsRegularFont(12)
- desc.textColor = .init(hex: "#5C5E66")
- desc.numberOfLines = 0
- desc.attributedText = attrStr
- container.addSubview(desc)
- desc.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(13)
- make.top.equalTo(title.snp.bottom).offset(6)
- make.trailing.equalTo(match.snp.leading).offset(-13)
- }
-
- let buttonText: String = .init(key: "C70053")
- let buttonAttrStr = NSMutableAttributedString(string: buttonText,
- attributes: [.foregroundColor: UIColor.white,
- .font: UIFont.poppinsSemiBoldFont(13)])
- let randomRange = (buttonAttrStr.string as NSString).range(of: "(Random)")
- buttonAttrStr.addAttributes([.foregroundColor: UIColor.white.withAlphaComponent(0.81),
- .font: UIFont.poppinsRegularFont(12)], range: randomRange)
- let matchButton = UIButton()
- matchButton.layer.cornerRadius = 12
- matchButton.clipsToBounds = true
- matchButton.setAttributedTitle(buttonAttrStr, for: .normal)
- matchButton.setBackgroundImage(commonGradientBg, for: .normal)
- matchButton.addTarget(self, action: #selector(handleMatchClick), for: .touchUpInside)
- container.addSubview(matchButton)
- matchButton.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(13)
- make.trailing.equalToSuperview().offset(-13)
- make.bottom.equalToSuperview().offset(-13)
- make.height.equalTo(40)
- }
-
- return container
- }
-
- private func buildPkMatch() -> UIView {
- let container = UIView()
-
- let bg = UIImageView()
- bg.image = UIImage(named: "icon_line_match_bg")
- container.addSubview(bg)
- bg.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- let match = UIImageView()
- let path = Bundle.main.path(forResource: "icon_line_pk_match", ofType: "webp")
- if let path {
- match.sd_setImage(with: URL(fileURLWithPath: path))
- } else {
- match.isHidden = true
- }
- container.addSubview(match)
- match.snp.makeConstraints { make in
- make.top.equalToSuperview().offset(5)
- make.trailing.equalToSuperview().offset(-5)
- make.width.equalTo(80)
- make.height.equalTo(76)
- }
-
- let pk = UIImageView()
- pk.setContentHuggingPriority(.defaultHigh, for: .horizontal)
- pk.image = .init(named: "icon_line_pk")
- container.addSubview(pk)
- pk.snp.makeConstraints { make in
- make.centerY.equalTo(match)
- make.trailing.equalTo(match.snp.leading).offset(-3)
- }
-
- let avatar = UIImageView()
- avatar.layer.cornerRadius = 18
- avatar.layer.borderColor = UIColor.white.cgColor
- avatar.layer.borderWidth = 1
- avatar.clipsToBounds = true
- if let myAvatar = UserDefaults.avatar {
- avatar.sd_setImage(with: URL(string: myAvatar))
- }
- container.addSubview(avatar)
- avatar.snp.makeConstraints { make in
- make.centerY.equalTo(match)
- make.trailing.equalTo(pk.snp.leading).offset(-12)
- make.width.height.equalTo(36)
- }
-
- let textContainer = UIView()
- container.addSubview(textContainer)
- textContainer.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(13)
- make.centerY.equalTo(match)
- make.trailing.equalTo(avatar.snp.leading).offset(-13)
- }
-
- let title = UILabel()
- title.text = .init(key: "C70060")
- title.font = .poppinsBoldFont(20)
- title.textColor = .init(hex: "#141924")
- textContainer.addSubview(title)
- title.snp.makeConstraints { make in
- make.leading.equalToSuperview()
- make.top.equalToSuperview()
- make.trailing.equalToSuperview()
- }
-
- let text: String = .init(key: "C70061", 15)
- let attrStr = NSMutableAttributedString(string: text)
- let range = (attrStr.string as NSString).range(of: "15s")
- attrStr.addAttributes([.foregroundColor: UIColor.init(hex: "#4363FF")], range: range)
- let desc = UILabel()
- desc.font = .poppinsRegularFont(12)
- desc.textColor = .init(hex: "#5C5E66")
- desc.numberOfLines = 0
- desc.attributedText = attrStr
- textContainer.addSubview(desc)
- desc.snp.makeConstraints { make in
- make.leading.equalToSuperview()
- make.top.equalTo(title.snp.bottom).offset(6)
- make.trailing.equalToSuperview()
- make.bottom.equalToSuperview()
- }
-
- let matchButton = UIButton()
- matchButton.setTitle(.init(key: "C70015"), for: .normal)
- matchButton.setTitleColor(.white, for: .normal)
- matchButton.titleLabel?.font = .poppinsSemiBoldFont(13)
- matchButton.layer.cornerRadius = 12
- matchButton.clipsToBounds = true
- matchButton.setBackgroundImage(commonGradientBg, for: .normal)
- matchButton.addTarget(self, action: #selector(handleMatchClick), for: .touchUpInside)
- container.addSubview(matchButton)
- matchButton.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(13)
- make.trailing.equalToSuperview().offset(-13)
- make.bottom.equalToSuperview().offset(-13)
- make.height.equalTo(40)
- }
-
- return container
- }
-
- private func buildList() -> UIView {
- let container = UIView()
-
- userTitle.text = ""
- userTitle.textColor = .init(hex: "#5C5E66")
- userTitle.font = .poppinsRegularFont(15)
- container.addSubview(userTitle)
- userTitle.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(12)
- make.top.equalToSuperview()
- }
-
- noMoreDataView.isHaveData = true
- noMoreDataView.tipLab.text = .init(key: "C70010")
-
- let header = MJRefreshNormalHeader { [weak self] in
- guard let self else { return }
- self.reload()
- }
- header.lastUpdatedTimeLabel?.isHidden = true
- header.stateLabel?.isHidden = true
- tableView.mj_header = header
-
- let footer = MJRefreshAutoNormalFooter(refreshingTarget: self, refreshingAction: #selector(loadNext))
- footer.setTitle("", for: .noMoreData)
- tableView.mj_footer = footer
-
- tableView.register(MOLineUserListCell.self, forCellReuseIdentifier: MOLineUserListCell.className())
- tableView.separatorColor = .clear
- tableView.delegate = self
- tableView.dataSource = self
- tableView.allowsSelection = false
- tableView.backgroundView = noMoreDataView
- container.addSubview(tableView)
- tableView.snp.makeConstraints { make in
- make.leading.equalToSuperview()
- make.trailing.equalToSuperview()
- make.bottom.equalToSuperview()
- make.top.equalTo(userTitle.snp.bottom)
- }
-
- return container
- }
- }
- //import SwiftUI
- //
- //struct MOLineUserListViewPreview: UIViewRepresentable {
- // func makeUIView(context: Context) -> some UIView {
- // let viewModel = MOLineViewModel()
- // let view = UIView()
- // view.backgroundColor = .black
- // let list = MOLineUserListView(viewModel, nil)
- // view.addSubview(list)
- // list.snp.makeConstraints { make in
- // make.leading.trailing.centerY.equalToSuperview()
- // }
- //
- // return view
- // }
- //
- // func updateUIView(_ uiView: UIViewType, context: Context) { }
- //}
- //
- //#Preview {
- // MOLineUserListViewPreview()
- //}
|