// // MPPositive_SearchResultShowViewController.swift // MusicPlayer // // Created by Mr.Zhou on 2024/5/12. // import UIKit ///搜索结果控制器 class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate { //MARK: - 导航View中的控件 //顶部搜索textField private lazy var searchTextField:UITextField = { let textField = UITextField() textField.delegate = self textField.font = .systemFont(ofSize: 14*width, weight: .regular) textField.textColor = .white //设置一个富文本占位符 let attributedText = NSAttributedString(string: "Search songs,artists,playlists", attributes: [.font:UIFont.systemFont(ofSize: 14*width, weight: .regular), .foregroundColor:UIColor(hex: "#666666")]) textField.attributedPlaceholder = attributedText return textField }() //删除文本按钮 private lazy var deleteBtn:UIButton = { let btn:UIButton = .init() btn.setBackgroundImage(UIImage(named: "Search_Delete'logo"), for: .normal) btn.addTarget(self, action: #selector(deleteTextClick(_ :)), for: .touchUpInside) return btn }() //cancel按钮 private lazy var cancelBtn:UIButton = { let btn:UIButton = .init() btn.setTitle("Cancel", for: .normal) btn.setTitleColor(.init(hex: "#FFFFFF", alpha: 0.85), for: .normal) btn.titleLabel?.font = .systemFont(ofSize: 14*width, weight: .regular) btn.addTarget(self, action: #selector(backPopClick(_ :)), for: .touchUpInside) return btn }() //搜索限定计时器 private var debounceTimer: Timer? //对用户展示的搜索建议组 private var suggestionList:MPPositive_SearchSuggestionItemListModel!{ didSet{ DispatchQueue.main.async { [weak self] in guard let self = self else {return} if suggestionList != nil { //搜索建议组存在,将其显现 suggestionView.isHidden = false suggestionView.suggestions = suggestionList.attributedTexts MP_AnalyticsManager.shared.search_sug_showAction() }else { suggestionView.isHidden = true suggestionView.suggestions = nil } } } } //搜索文本 private var searchText:String? //MARK: - 搜索建议View private lazy var suggestionView:MPPositive_SearchSuggestionsView = .init(frame: .zero) //MARK: - 结果展示View private lazy var resultsShowView:MPPositive_SearchResultsShowView = .init(frame: .zero) override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } init(_ text:String) { super.init(nibName: nil, bundle: nil) searchTextField.text = text searchText = text resultsShowView.loadModel = .init(text) MP_AnalyticsManager.shared.search_sug_clickAction(text) } required init?(coder: NSCoder) { super.init(coder: coder) } override func viewDidLoad() { super.viewDidLoad() setTitle("Result") setPopBtn() configure() suggestionView.selectedTextBlock = { [weak self] (text) in guard let self = self else {return} searchText = text searchTextField.text = text resultsShowView.loadModel = .init(text) MP_AnalyticsManager.shared.search_sug_clickAction(text) suggestionView.isHidden = true } resultsShowView.scrollBlock = { [weak self] in self?.view?.endEditing(true) } resultsShowView.chooseItemBlock = { [weak self] (item) in guard let self = self else {return} switch item.item.itemType { case .artist: //用户查看艺术家 let artistVC = MPPositive_ArtistShowViewController(item.item.browseId ?? "") navigationController?.pushViewController(artistVC, animated: true) case .list: //列表专辑 let listVC = MPPositive_ListShowViewController(item.item.browseId ?? "", params: "", title: item.title ?? "", subtitle: item.subtitle ?? "") navigationController?.pushViewController(listVC, animated: true) case .single: MPPositive_Debouncer.shared.call { [weak self] in guard let self = self else {return} //单曲/视频跳转 //触发next请求,优先获取列表全部单曲基础数据(不完善) MP_NetWorkManager.shared.requestNextList(item.item.playListId ?? "", videoId: item.item.videoId ?? ""){ [weak self] listSongs in guard let self = self else {return} //回掉的数据并不完善,生成一个playerloadViewModel let lodaViewModel = MPPositive_PlayerLoadViewModel(listSongs, currentVideoId: item.item.videoId ?? "") lodaViewModel.improveData(item.item.videoId ?? "") MP_PlayerManager.shared.loadPlayer = lodaViewModel NotificationCenter.notificationKey.post(notificationName: .pup_player_vc) } } case .none: break } } resultsShowView.moreBlock = { [weak self] (itemView) in guard let self = self else {return} MPPositive_Debouncer.shared.call { MP_NetWorkManager.shared.requestNextList("", videoId: itemView.item.videoId ?? ""){ [weak self] listSongs in guard let first = listSongs.first else {return} let group = DispatchGroup() group.enter() improveDataforLycirsAndRelated(first) {[weak self] (result) in first.lyricsID = result.0 first.relatedID = result.1 group.leave() } group.enter() //补全资源路径组和封面路径组 improveDataforResouceAndCover(first) {[weak self] resourceUrls, coverUrls in first.resourceUrls = resourceUrls.0 first.itags = resourceUrls.1 first.mimeTypes = resourceUrls.2 first.coverUrls = coverUrls group.leave() } group.notify(queue: .main, execute: { [weak self] in MPPositive_ModalType = .MoreOperations let moreVC = MPPositive_MoreSongOperationsViewController(first) moreVC.transitioningDelegate = self moreVC.modalPresentationStyle = .custom self?.present(moreVC, animated: true) }) } } } errorBlock = { [weak self] in MP_HUD.hideNow() self?.resultsShowView.isHidden = false self?.resultsShowView.emptyImageView.isHidden = false } } deinit{ debounceTimer = nil } func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) } private func configure() { //配置导航栏 let searchView = createSearchView() navView.addSubview(searchView) searchView.snp.makeConstraints { make in make.width.equalTo(275*width) make.height.equalTo(40*width) make.centerY.equalToSuperview() make.left.equalToSuperview().offset(18*width) } navView.addSubview(cancelBtn) cancelBtn.snp.makeConstraints { make in make.right.equalToSuperview().offset(-18*width) make.centerY.equalToSuperview() } //添加suggestionView view.addSubview(suggestionView) suggestionView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() make.top.equalTo(navView.snp.bottom) } suggestionView.isHidden = true //添加resultsShowView view.addSubview(resultsShowView) resultsShowView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() make.top.equalTo(navView.snp.bottom) } resultsShowView.isHidden = true } //生成一个顶部搜索框 private func createSearchView() -> UIView{ let searchView:UIView = UIView() searchView.backgroundColor = .init(hex: "#000000") searchView.isUserInteractionEnabled = true searchView.layer.masksToBounds = true searchView.layer.cornerRadius = 20*width //添加一个icon let iconImageView = UIImageView(image: .init(named: "Search_ICON'logo")) searchView.addSubview(iconImageView) iconImageView.snp.makeConstraints { make in make.height.width.equalTo(24*width) make.left.equalToSuperview().offset(8*width) make.centerY.equalToSuperview() } searchView.addSubview(deleteBtn) deleteBtn.snp.makeConstraints { make in make.height.width.equalTo(24*width) make.right.equalToSuperview().offset(-12*width) make.centerY.equalToSuperview() } //添加textField searchView.addSubview(searchTextField) searchTextField.snp.makeConstraints { make in make.top.bottom.equalToSuperview() make.left.equalTo(iconImageView.snp.right).offset(14*width) make.right.equalTo(deleteBtn.snp.left).offset(-12*width) } return searchView } //删除输入框文本 @objc private func deleteTextClick(_ sender:UIButton) { //清空文本框内容,并隐藏两个View searchTextField.text = "" searchText = "" suggestionView.suggestions = nil resultsShowView.loadModel = nil } //取消并返回上一页 @objc private func backPopClick(_ sender:UIButton) { navigationController?.popViewController(animated: true) } } //MARK: - UITextFieldDelegate extension MPPositive_SearchResultShowViewController:UITextFieldDelegate { func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let text = (textField.text! as NSString).replacingCharacters(in: range, with: string) guard text.count <= 30 else { return false } //文本出现变化,隐藏结果View resultsShowView.isHidden = true if text.isEmpty { self.suggestionList = nil cancelDebounceTimer() }else { //触发网络请求 loadSearchSuggestions(text) } return true } //避免重复请求 private func cancelDebounceTimer() { debounceTimer?.invalidate() debounceTimer = nil } //当用户输入文本通过后进行检索 private func loadSearchSuggestions(_ text:String) { cancelDebounceTimer() //停止输入0.8秒后调用 debounceTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in self?.searchText = text self?.fetchSearchSuggestions(text) } } //获取建议组 private func fetchSearchSuggestions(_ text:String) { MP_NetWorkManager.shared.requestSearchSuggestions(text) { [weak self] (result) in self?.suggestionList = .init(self?.searchText ?? "", suggestions: result) } } func textFieldShouldReturn(_ textField: UITextField) -> Bool { //判断textField是否存在文本 if let text = textField.text, text.isEmpty != true { self.searchText = text //用户输入了文本 resultsShowView.loadModel = .init(text) MP_AnalyticsManager.shared.search_sug_clickAction(text) suggestionView.isHidden = true //停止输入 view.endEditing(true) return true }else { return false } } }