diff --git a/Thimra/Base/Define/SPUserDefaultsKey.swift b/Thimra/Base/Define/SPUserDefaultsKey.swift index e597066..bd5c431 100644 --- a/Thimra/Base/Define/SPUserDefaultsKey.swift +++ b/Thimra/Base/Define/SPUserDefaultsKey.swift @@ -9,3 +9,6 @@ import UIKit ///登录的用户信息 let kSPLoginTokenDefaultsKey = "kSPLoginTokenDefaultsKey" + +///首页搜索记录 +let kSPHomeSearchHistoryDefaultsKey = "kSPHomeSearchHistoryDefaultsKey" diff --git a/Thimra/Base/Extension/UIColor+SPAdd.swift b/Thimra/Base/Extension/UIColor+SPAdd.swift index 075beb2..222e126 100644 --- a/Thimra/Base/Extension/UIColor+SPAdd.swift +++ b/Thimra/Base/Extension/UIColor+SPAdd.swift @@ -24,7 +24,7 @@ extension UIColor { static func placeholderColor() -> UIColor { - return .colorFFFFFF(alpha: 0.22) + return .colorFFFFFF(alpha: 0.6) } } @@ -124,5 +124,25 @@ extension UIColor { static func colorD9D9D9(alpha: CGFloat = 1) -> UIColor { return color(hex: 0xD9D9D9, alpha: alpha) } + + static func colorEAF7FF(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0xEAF7FF, alpha: alpha) + } + + static func colorFF4B5A(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0xFF4B5A, alpha: alpha) + } + + static func color1E1F2C(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0x1E1F2C, alpha: alpha) + } + + static func colorE7F5FF(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0xE7F5FF, alpha: alpha) + } + + static func colorAFB8BF(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0xAFB8BF, alpha: alpha) + } } diff --git a/Thimra/Base/Networking/API/SPHomeAPI.swift b/Thimra/Base/Networking/API/SPHomeAPI.swift index 48ae294..711b21a 100644 --- a/Thimra/Base/Networking/API/SPHomeAPI.swift +++ b/Thimra/Base/Networking/API/SPHomeAPI.swift @@ -73,5 +73,16 @@ class SPHomeAPI: NSObject { } + ///热门搜索 + static func requestHotSearchList(completer: ((_ list: [SPShortModel]?) -> Void)?) { + var param = SPNetworkParameters(path: "/search/hots") + param.method = .get + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse>) in + completer?(response.data?.list) + } + + } + } diff --git a/Thimra/Base/View/SPTextField.swift b/Thimra/Base/View/SPTextField.swift index 2e4d099..122c0b4 100644 --- a/Thimra/Base/View/SPTextField.swift +++ b/Thimra/Base/View/SPTextField.swift @@ -87,7 +87,7 @@ class SPTextField: UITextField { // var iq = self.iq // iq.enableMode = .enabled - self.textColor = .colorFFFFFF(alpha: 0.9) + self.textColor = .colorFFFFFF() } required init?(coder: NSCoder) { diff --git a/Thimra/Class/Home/Controller/SPSearchViewController.swift b/Thimra/Class/Home/Controller/SPSearchViewController.swift index f9d7112..6bd3127 100644 --- a/Thimra/Class/Home/Controller/SPSearchViewController.swift +++ b/Thimra/Class/Home/Controller/SPSearchViewController.swift @@ -8,7 +8,11 @@ import UIKit class SPSearchViewController: SPViewController { - + + + private lazy var viewModel: SPSearchViewModel = SPSearchViewModel() + + //MARK: UI属性 private lazy var backButton: UIButton = { let button = UIButton(type: .custom) button.setImage(UIImage(named: "arrow_left_icon_01"), for: .normal) @@ -28,12 +32,18 @@ class SPSearchViewController: SPViewController { ///搜索首页 private lazy var homeView: SPSearchHomeView = { let view = SPSearchHomeView() + view.viewModel = self.viewModel + view.searchTextBlock = { [weak self] text in + self?.searchInputView.textField.text = text + self?.textDidChange(text: text) + } return view }() ///联想页面 private lazy var associativeView: SPSearchAssociativeView = { let view = SPSearchAssociativeView() + view.viewModel = self.viewModel view.isHidden = true return view }() @@ -48,6 +58,8 @@ class SPSearchViewController: SPViewController { override func viewDidLoad() { super.viewDidLoad() + searchInputView.textField.becomeFirstResponder() + _setupUI() } diff --git a/Thimra/Class/Home/View/SPHomeHotSearchCell.swift b/Thimra/Class/Home/View/SPHomeHotSearchCell.swift new file mode 100644 index 0000000..9666c81 --- /dev/null +++ b/Thimra/Class/Home/View/SPHomeHotSearchCell.swift @@ -0,0 +1,84 @@ +// +// SPHomeHotSearchCell.swift +// Thimra +// +// Created by Overseas on 2025/4/22. +// + +import UIKit + +class SPHomeHotSearchCell: SPTableViewCell { + + var model: SPShortModel? { + didSet { + coverImageView.sp_setImage(url: model?.image_url) + + titleLabel.text = model?.name + + desLabel.text = model?.sp_description + } + } + + //MARK: UI属性 + private lazy var coverImageView: SPImageView = { + let imageView = SPImageView() + imageView.layer.cornerRadius = 6 + imageView.layer.masksToBounds = true + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 14) + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 12) + label.textColor = .colorAFB8BF() + label.numberOfLines = 2 + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + _setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SPHomeHotSearchCell { + + private func _setupUI() { + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + contentView.addSubview(desLabel) + + coverImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(12) + make.width.equalTo(74) + make.height.equalTo(100) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(12) + make.right.lessThanOrEqualToSuperview().offset(-12) + make.top.equalTo(coverImageView) + } + + desLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.top.equalTo(titleLabel.snp.bottom).offset(5) + make.right.lessThanOrEqualToSuperview().offset(-12) + } + } + +} diff --git a/Thimra/Class/Home/View/SPHomeHotSearchView.swift b/Thimra/Class/Home/View/SPHomeHotSearchView.swift new file mode 100644 index 0000000..a1ad5da --- /dev/null +++ b/Thimra/Class/Home/View/SPHomeHotSearchView.swift @@ -0,0 +1,138 @@ +// +// SPHomeHotSearchView.swift +// Thimra +// +// Created by Overseas on 2025/4/22. +// + +import UIKit + +class SPHomeHotSearchView: UIView { + + override var intrinsicContentSize: CGSize { + return CGSize(width: kSPScreenWidth, height: kSPScreenHeight) + } + + private var dataArr: [SPShortModel] = [] + + private lazy var bgView: UIView = { + let view = UIImageView(image: UIImage(named: "hot_search_bg_image")) + view.isUserInteractionEnabled = true + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 16) + label.textColor = .colorFFFFFF() + label.text = "Top Search".localized + return label + }() + + private lazy var hotIconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "hot_icon_02")) + return imageView + }() + + private lazy var tableView: SPTableView = { + let tableView = SPTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 112 + tableView.separatorStyle = .none + tableView.showsVerticalScrollIndicator = false + tableView.showsHorizontalScrollIndicator = false + tableView.contentInset = .init(top: 0, left: 0, bottom: kSPTabbarSafeBottomMargin, right: 0) + SPHomeHotSearchCell.registerCell(tableView: tableView) + return tableView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + _setupUI() + + requestDataArr() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SPHomeHotSearchView { + + private func _setupUI() { + addSubview(bgView) + bgView.addSubview(titleLabel) + bgView.addSubview(hotIconImageView) + bgView.addSubview(tableView) + + bgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.centerY.equalToSuperview() + make.top.equalToSuperview() + make.bottom.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.top.equalToSuperview().offset(20) + } + + hotIconImageView.snp.makeConstraints { make in + make.centerY.equalTo(titleLabel) + make.left.equalTo(titleLabel.snp.right).offset(4) + } + + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(50) + } + + } + + +} + +//MARK: -------------- UITableViewDelegate & UITableViewDataSource -------------- +extension SPHomeHotSearchView: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = SPHomeHotSearchCell.dequeueReusableCell(tableView: tableView, indexPath: indexPath) + cell.model = dataArr[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.dataArr.count + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let model = dataArr[indexPath.row] + + let vc = SPPlayerDetailViewController() + vc.shortPlayId = model.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } + +} + +extension SPHomeHotSearchView { + + private func requestDataArr() { + + SPHomeAPI.requestHotSearchList { [weak self] list in + guard let self = self else { return } + + if let list = list { + self.dataArr = list + self.tableView.reloadData() + } + + } + + } + +} diff --git a/Thimra/Class/Home/View/SPHomeSearchHistoryView.swift b/Thimra/Class/Home/View/SPHomeSearchHistoryView.swift new file mode 100644 index 0000000..57b975e --- /dev/null +++ b/Thimra/Class/Home/View/SPHomeSearchHistoryView.swift @@ -0,0 +1,129 @@ +// +// SPHomeSearchHistoryView.swift +// Thimra +// +// Created by Overseas on 2025/4/22. +// + +import UIKit + +class SPHomeSearchHistoryView: UIView { + + + override var intrinsicContentSize: CGSize { + return CGSize(width: kSPScreenWidth, height: 30 + self.tagView.intrinsicContentSize.height) + } + + var viewModel: SPSearchViewModel? { + didSet { + viewModel?.addObserver(self, forKeyPath: "searchHistoryArr", context: nil) + + self.tagView.reloadData() + + self.invalidateIntrinsicContentSize() + } + } + + var searchTextBlock: ((_ text: String) -> Void)? + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontBold(ofSize: 16) + label.textColor = .colorE7F5FF() + label.text = "Historical search".localized + return label + }() + + private lazy var deleteButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "delete_icon_03"), for: .normal) + button.addTarget(self, action: #selector(handleDeleteButton), for: .touchUpInside) + return button + }() + + private lazy var tagView: JXTagView = { + let view = JXTagView() + view.delegate = self + view.dataSource = self + view.tagBorderWidth = 1 + view.tagBorderColor = .colorFFFFFF(alpha: 0.2) + view.tagHeight = 20 + view.tagCornerRadius = 10 + view.tagBackgroundColor = .clear + view.textFont = .fontRegular(ofSize: 12) + view.textColor = .colorFFFFFF(alpha: 0.8) + view.textMargin = 8 + view.tagHorizontalGap = 12 + view.tagVerticalGap = 12 + view.leftAndRightMargin = 16 + return view + }() + + deinit { + viewModel?.removeObserver(self, forKeyPath: "searchHistoryArr") + } + + override init(frame: CGRect) { + super.init(frame: frame) + + _setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "searchHistoryArr" { + self.tagView.reloadData() + } + } + + + @objc private func handleDeleteButton() { + self.viewModel?.cleanSearchHistory() + } +} + +extension SPHomeSearchHistoryView { + + private func _setupUI() { + addSubview(titleLabel) + addSubview(deleteButton) + addSubview(tagView) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.centerY.equalTo(deleteButton) + } + + deleteButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-16) + make.top.equalToSuperview() + } + + tagView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalToSuperview() +// make.top.equalTo(self.titleLabel.snp.bottom).offset(12) + make.top.equalToSuperview().offset(30) + } + } + +} + +//MARK: -------------- JXTagViewDelegate & JXTagViewDataSource -------------- +extension SPHomeSearchHistoryView: JXTagViewDelegate, JXTagViewDataSource { + func jx_tagView(tagView: JXTagView, titleForIndex index: Int) -> String? { + return viewModel?.searchHistoryArr[index] + } + + func jx_number(in tagView: JXTagView) -> Int { + return viewModel?.searchHistoryArr.count ?? 0 + } + + func jx_tagView(tagView: JXTagView, didSelectedTagAt index: Int) { + guard let text = viewModel?.searchHistoryArr[index] else { return } + self.searchTextBlock?(text) + } +} diff --git a/Thimra/Class/Home/View/SPSearchAssociativeCell.swift b/Thimra/Class/Home/View/SPSearchAssociativeCell.swift index 7de53b1..1dde9c9 100644 --- a/Thimra/Class/Home/View/SPSearchAssociativeCell.swift +++ b/Thimra/Class/Home/View/SPSearchAssociativeCell.swift @@ -19,10 +19,10 @@ class SPSearchAssociativeCell: SPTableViewCell { titleLabel.attributedText = string } else { let string = NSMutableAttributedString(string: model?.name ?? "") - string.color = .color8A899F() + string.color = .colorEAF7FF() - if let range = model?.name?.ocString().range(of: searchText ?? "") { - string.setColor(.colorFFFFFF(), range: range) + if let range = model?.name?.lowercased().ocString().range(of: (searchText ?? "").lowercased()) { + string.setColor(.colorFF4B5A(), range: range) } titleLabel.attributedText = string @@ -33,6 +33,8 @@ class SPSearchAssociativeCell: SPTableViewCell { private lazy var iconImageView: UIImageView = { let imageView = UIImageView(image: UIImage(named: "play_icon_03")) + imageView.setContentHuggingPriority(.required, for: .horizontal) + imageView.setContentCompressionResistancePriority(.required, for: .horizontal) return imageView }() @@ -62,12 +64,12 @@ extension SPSearchAssociativeCell { iconImageView.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.left.equalToSuperview().offset(15) + make.left.equalToSuperview().offset(16) } titleLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.left.equalToSuperview().offset(40) + make.left.equalToSuperview().offset(36) make.right.lessThanOrEqualToSuperview().offset(-15) } } diff --git a/Thimra/Class/Home/View/SPSearchAssociativeView.swift b/Thimra/Class/Home/View/SPSearchAssociativeView.swift index 9c001b4..8fa2dd0 100644 --- a/Thimra/Class/Home/View/SPSearchAssociativeView.swift +++ b/Thimra/Class/Home/View/SPSearchAssociativeView.swift @@ -9,6 +9,7 @@ import UIKit class SPSearchAssociativeView: UIView { + var viewModel: SPSearchViewModel? private(set) lazy var searchText: String = "" @@ -18,8 +19,11 @@ class SPSearchAssociativeView: UIView { let tableView = SPTableView(frame: .zero, style: .plain) tableView.delegate = self tableView.dataSource = self - tableView.rowHeight = 50 + tableView.rowHeight = 60 + tableView.separatorColor = .color1E1F2C() + tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: kSPTabbarSafeBottomMargin, right: 0) + tableView.keyboardDismissMode = .onDrag SPSearchAssociativeCell.registerCell(tableView: tableView) return tableView }() @@ -71,6 +75,7 @@ extension SPSearchAssociativeView: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let model = self.dataArr[indexPath.row] + self.viewModel?.addSearchHistory(text: self.searchText) let vc = SPPlayerDetailViewController() vc.shortPlayId = model.short_play_id diff --git a/Thimra/Class/Home/View/SPSearchHomeView.swift b/Thimra/Class/Home/View/SPSearchHomeView.swift index 468ecc2..1751aea 100644 --- a/Thimra/Class/Home/View/SPSearchHomeView.swift +++ b/Thimra/Class/Home/View/SPSearchHomeView.swift @@ -8,7 +8,89 @@ import UIKit class SPSearchHomeView: UIView { - + + + var viewModel: SPSearchViewModel? { + didSet { + viewModel?.addObserver(self, forKeyPath: "searchHistoryArr", context: nil) + + historyView.viewModel = self.viewModel + + _updateUI() + } + } + + var searchTextBlock: ((_ text: String) -> Void)? + + //MARK: UI属性 + private lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .vertical + view.spacing = 28 + return view + }() + + private lazy var historyView: SPHomeSearchHistoryView = { + let view = SPHomeSearchHistoryView() + view.searchTextBlock = { [weak self] text in + self?.searchTextBlock?(text) + } + return view + }() + + ///热门搜索 + private lazy var hotSearchView: SPHomeHotSearchView = { + let view = SPHomeHotSearchView() + return view + }() + + deinit { + viewModel?.removeObserver(self, forKeyPath: "searchHistoryArr") + } + + override init(frame: CGRect) { + super.init(frame: frame) + + stackView.addArrangedSubview(historyView) + + _setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "searchHistoryArr" { + _updateUI() + } + } + + +} + +extension SPSearchHomeView { + + private func _setupUI() { + addSubview(stackView) + + stackView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(10) + make.left.right.bottom.equalToSuperview() +// make.edges.equalToSuperview() + } + } + + private func _updateUI() { + stackView.removeAllArrangedSubview() + + if (self.viewModel?.searchHistoryArr.count ?? 0) > 0 { + self.stackView.addArrangedSubview(self.historyView) + } + + self.stackView.addArrangedSubview(self.hotSearchView) + } + } diff --git a/Thimra/Class/Home/View/SPSearchInputView.swift b/Thimra/Class/Home/View/SPSearchInputView.swift index 696ccb5..1489e47 100644 --- a/Thimra/Class/Home/View/SPSearchInputView.swift +++ b/Thimra/Class/Home/View/SPSearchInputView.swift @@ -10,7 +10,7 @@ import UIKit class SPSearchInputView: UIView { override var intrinsicContentSize: CGSize { - return CGSize(width: kSPScreenWidth, height: 38) + return CGSize(width: kSPScreenWidth, height: 36) } var placeholder: String? { @@ -31,7 +31,8 @@ class SPSearchInputView: UIView { private(set) lazy var textField: SPTextField = { let textField = SPTextField() - textField.font = .fontRegular(ofSize: 12) + textField.font = .fontMedium(ofSize: 14) + textField.sp_placeholderFont = .fontRegular(ofSize: 14) textField.returnKeyType = .search textField.textDidChange = { [weak self] text in self?.textDidChange?(text) @@ -41,9 +42,12 @@ class SPSearchInputView: UIView { override init(frame: CGRect) { super.init(frame: frame) - layer.cornerRadius = 19 + layer.cornerRadius = 8 layer.masksToBounds = true - backgroundColor = .colorFFFFFF(alpha: 0.1) + backgroundColor = .colorFFFFFF(alpha: 0.2) + + setContentHuggingPriority(.required, for: .vertical) + setContentCompressionResistancePriority(.required, for: .vertical) _setupUI() @@ -62,13 +66,13 @@ extension SPSearchInputView { addSubview(textField) iconImageView.snp.makeConstraints { make in - make.left.equalToSuperview().offset(15) + make.left.equalToSuperview().offset(16) make.centerY.equalToSuperview() } textField.snp.makeConstraints { make in make.top.bottom.equalToSuperview() - make.left.equalTo(iconImageView.snp.right).offset(7) + make.left.equalTo(iconImageView.snp.right).offset(8) make.right.equalToSuperview().offset(-15) } } diff --git a/Thimra/Class/Home/ViewModel/SPHomeViewModel.swift b/Thimra/Class/Home/ViewModel/SPHomeViewModel.swift index 3ec1b4c..5cc2670 100644 --- a/Thimra/Class/Home/ViewModel/SPHomeViewModel.swift +++ b/Thimra/Class/Home/ViewModel/SPHomeViewModel.swift @@ -14,4 +14,6 @@ class SPHomeViewModel: NSObject { ///历史记录 var playHistoryArr: [SPShortModel]? + + } diff --git a/Thimra/Class/Home/ViewModel/SPSearchViewModel.swift b/Thimra/Class/Home/ViewModel/SPSearchViewModel.swift index 54c82da..0d6c8f3 100644 --- a/Thimra/Class/Home/ViewModel/SPSearchViewModel.swift +++ b/Thimra/Class/Home/ViewModel/SPSearchViewModel.swift @@ -9,6 +9,53 @@ import UIKit class SPSearchViewModel: NSObject { + ///搜索历史 + @objc dynamic var searchHistoryArr: [String] = SPSearchViewModel.getSearchHistory() + + + func addSearchHistory(text: String) { + guard text.count > 0 else { return } + + SPSearchViewModel.addSearchHistory(text: text) + searchHistoryArr = SPSearchViewModel.getSearchHistory() + } + + func cleanSearchHistory() { + SPSearchViewModel.cleanSearchHistory() + searchHistoryArr.removeAll() + } +} + +extension SPSearchViewModel { + + ///添加历史记录 + static func addSearchHistory(text: String) { + var arr = getSearchHistory() + for (i, value) in arr.enumerated() { + if value == text { + arr.remove(at: i) + break + } + } + arr.insert(text, at: 0) + if arr.count > 10 { + arr.removeLast() + } + UserDefaults.standard.set(arr, forKey: kSPHomeSearchHistoryDefaultsKey) + UserDefaults.standard.synchronize() + } + + ///获取历史记录 + static func getSearchHistory() -> [String] { + let arr = UserDefaults.standard.object(forKey: kSPHomeSearchHistoryDefaultsKey) as? [String] + return arr ?? [] + } + + ///清空历史记录 + static func cleanSearchHistory() { + UserDefaults.standard.set([], forKey: kSPHomeSearchHistoryDefaultsKey) + UserDefaults.standard.synchronize() + } } diff --git a/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Contents.json b/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Contents.json index 2fedf34..397dc97 100644 --- a/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Contents.json +++ b/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Contents.json @@ -5,11 +5,12 @@ "scale" : "1x" }, { - "filename" : "nav_back@2x.png", + "filename" : "Frame 70@2x.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "Frame 70@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Frame 70@2x.png b/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Frame 70@2x.png new file mode 100644 index 0000000..a89b095 Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Frame 70@2x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Frame 70@3x.png b/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Frame 70@3x.png new file mode 100644 index 0000000..0ec346f Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Frame 70@3x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/nav_back@2x.png b/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/nav_back@2x.png deleted file mode 100644 index 633b9e6..0000000 Binary files a/Thimra/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/nav_back@2x.png and /dev/null differ diff --git a/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Contents.json b/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Frame@2x.png b/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Frame@2x.png new file mode 100644 index 0000000..551327b Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Frame@2x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Frame@3x.png b/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Frame@3x.png new file mode 100644 index 0000000..246b90d Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/delete_icon_03.imageset/Frame@3x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Contents.json b/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Frame@2x.png b/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Frame@2x.png new file mode 100644 index 0000000..cd446c5 Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Frame@2x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Frame@3x.png b/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Frame@3x.png new file mode 100644 index 0000000..7230083 Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/hot_icon_02.imageset/Frame@3x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Contents.json b/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Contents.json index c9f3912..05827c5 100644 --- a/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Contents.json +++ b/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Contents.json @@ -5,12 +5,12 @@ "scale" : "1x" }, { - "filename" : "play@2x.png", + "filename" : "Frame 71@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "play@3x.png", + "filename" : "Frame 71@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Frame 71@2x.png b/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Frame 71@2x.png new file mode 100644 index 0000000..9a9f7d4 Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Frame 71@2x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Frame 71@3x.png b/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Frame 71@3x.png new file mode 100644 index 0000000..b7b98ee Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/Frame 71@3x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/play@2x.png b/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/play@2x.png deleted file mode 100644 index 2903cfb..0000000 Binary files a/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/play@2x.png and /dev/null differ diff --git a/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/play@3x.png b/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/play@3x.png deleted file mode 100644 index bd1931a..0000000 Binary files a/Thimra/Source/Assets.xcassets/icon/play_icon_03.imageset/play@3x.png and /dev/null differ diff --git a/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Contents.json b/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Contents.json index d4fc1b3..74b6aa5 100644 --- a/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Contents.json +++ b/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Contents.json @@ -5,12 +5,12 @@ "scale" : "1x" }, { - "filename" : "搜索图标@2x.png", + "filename" : "Frame 26@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "搜索图标@3x.png", + "filename" : "Frame 26@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Frame 26@2x.png b/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Frame 26@2x.png new file mode 100644 index 0000000..31e2fd3 Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Frame 26@2x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Frame 26@3x.png b/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Frame 26@3x.png new file mode 100644 index 0000000..3b3964c Binary files /dev/null and b/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/Frame 26@3x.png differ diff --git a/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/搜索图标@2x.png b/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/搜索图标@2x.png deleted file mode 100644 index e63fd6a..0000000 Binary files a/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/搜索图标@2x.png and /dev/null differ diff --git a/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/搜索图标@3x.png b/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/搜索图标@3x.png deleted file mode 100644 index 1bbdfdf..0000000 Binary files a/Thimra/Source/Assets.xcassets/icon/search_icon_02.imageset/搜索图标@3x.png and /dev/null differ diff --git a/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/Contents.json b/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/Contents.json new file mode 100644 index 0000000..6f02c5c --- /dev/null +++ b/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "背景图@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "背景图@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/背景图@2x.png b/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/背景图@2x.png new file mode 100644 index 0000000..b290bd5 Binary files /dev/null and b/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/背景图@2x.png differ diff --git a/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/背景图@3x.png b/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/背景图@3x.png new file mode 100644 index 0000000..209b8f8 Binary files /dev/null and b/Thimra/Source/Assets.xcassets/image/hot_search_bg_image.imageset/背景图@3x.png differ diff --git a/Thimra/Source/en.lproj/Localizable.strings b/Thimra/Source/en.lproj/Localizable.strings index 829fb57..4453b65 100644 --- a/Thimra/Source/en.lproj/Localizable.strings +++ b/Thimra/Source/en.lproj/Localizable.strings @@ -38,6 +38,8 @@ "Continue watching" = "Continue watching"; "More for you!" = "More for you!"; "More" = "More"; +"Historical search" = "Historical search"; +"Top Search" = "Top Search"; ///视频详情标题 "kPlayerDetailTitleString" = "Episode %@ / %@"; diff --git a/Thimra/Thirdparty/JXTagView/JXTagView.swift b/Thimra/Thirdparty/JXTagView/JXTagView.swift new file mode 100644 index 0000000..ac285bf --- /dev/null +++ b/Thimra/Thirdparty/JXTagView/JXTagView.swift @@ -0,0 +1,307 @@ +// +// JXTagView.swift +// YDLive +// +// Created by 曾觉新 on 2020/11/7. +// + +import UIKit + +@objc protocol JXTagViewDataSource: NSObjectProtocol { + + @objc optional func jx_tagView(tagView: JXTagView, titleForIndex index: Int) -> String? + @objc optional func jx_tagView(tagView: JXTagView, attributedTitleForIndex index: Int) -> NSAttributedString? + func jx_number(in tagView: JXTagView) -> Int + +} + +@objc protocol JXTagViewDelegate: NSObjectProtocol { + + @objc optional func jx_tagView(tagView: JXTagView, didSelectedTagAt index: Int) + + @objc optional func jx_tagView(tagView: JXTagView, enableForIndex index: Int) -> Bool + + @objc optional func jx_tagView(tagView: JXTagView, selectedForIndex index: Int) -> Bool +} + +class JXTagView: UIView { + enum LayoutDirection: Int { + case vertical = 0 + case horizontal = 1 + } + + + weak var delegate: JXTagViewDelegate? + weak var dataSource: JXTagViewDataSource? + + var layoutDirection: LayoutDirection = .vertical + + var tagBackgroundColor: UIColor = .systemBackground + var tagSelectedBackgroundColor: UIColor = .systemBackground + var tagDisabledBackgroundColor: UIColor = .systemBackground + + var textFont: UIFont = .systemFont(ofSize: 14) + + var textColor: UIColor = .colorFFFFFF() + var textSelectedColor: UIColor = .colorFFFFFF() + var textDisabledColor: UIColor = .gray + + var tagCornerRadius: CGFloat = 14 + + var tagBorderWidth: CGFloat = 0 + var tagBorderColor: UIColor? + var tagBorderSelectedColor: UIColor? + + /// 0表示自适应 + var tagWidth: CGFloat = 0 + var tagHeight: CGFloat = 28 + var tagMinWidth: CGFloat = 0 + ///文本边距 + var textMargin: CGFloat = 0 + ///横向间隙 + var tagHorizontalGap: CGFloat = 10 + ///纵向间隙 + var tagVerticalGap: CGFloat = 10 + ///左右边距 + var leftAndRightMargin: CGFloat = 20 + ///上下边距 + var topAndBottomMargin: CGFloat = 0 + ///当前选中的标签 + private(set) var selectedIndex: Int? + + private var oneReload = true + + //MARK:-------------- 黄金分割线 -------------- + private(set) var buttonArr: [UIButton] = [] + + private var contentHeight: CGFloat = 0 + + override var intrinsicContentSize: CGSize { + let height = (self.buttonArr.last?.frame.maxY ?? 0) + self.topAndBottomMargin + if layoutDirection == .vertical { + return CGSize(width: UIScreen.main.bounds.size.width, height: height) + } else { + let width = (self.buttonArr.last?.frame.maxX ?? 0) + self.leftAndRightMargin + return CGSize(width: width, height: height) + } + } + +// deinit { +// NotificationCenter.default.removeObserver(self) +// } + + override init(frame: CGRect) { + super.init(frame: frame) + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func layoutSubviews() { + super.layoutSubviews() + if oneReload { + self.reloadData() + oneReload = false + } + + self.buttonArr.enumerated().forEach { + let isEnble: Bool = self.delegate?.jx_tagView?(tagView: self, enableForIndex: $0) ?? true + let isSelected: Bool = self.delegate?.jx_tagView?(tagView: self, selectedForIndex: $0) ?? false + + let button = $1 + button.isSelected = isSelected + button.isEnabled = isEnble + + if isSelected { + button.layer.borderColor = tagBorderSelectedColor?.cgColor + } else { + button.layer.borderColor = tagBorderColor?.cgColor + } + button.setBackgroundImage(UIImage(color: tagBackgroundColor), for: .normal) + button.setBackgroundImage(UIImage(color: tagSelectedBackgroundColor), for: .selected) + button.setBackgroundImage(UIImage(color: tagSelectedBackgroundColor), for: [.highlighted, .selected]) + button.setBackgroundImage(UIImage(color: tagDisabledBackgroundColor), for: .disabled) + + } + } + + ///数据没有变化,只是变化状态 + func updateState() { + self.setNeedsLayout() + self.layoutIfNeeded() + } + + ///重置布局:当数据发生变化时,需要重置布局 + @objc func reloadData() { + selectedIndex = nil + for button in buttonArr { + button.removeFromSuperview() + } + buttonArr.removeAll() + + let number = self.dataSource?.jx_number(in: self) ?? 0 + if number == 0 { + self.invalidateIntrinsicContentSize() + return + } + + for index in 0...(number - 1) { + let text = self.dataSource?.jx_tagView?(tagView: self, titleForIndex: index) + let attributedTitle = self.dataSource?.jx_tagView?(tagView: self, attributedTitleForIndex: index) + let isEnble: Bool = self.delegate?.jx_tagView?(tagView: self, enableForIndex: index) ?? true + let isSelected: Bool = self.delegate?.jx_tagView?(tagView: self, selectedForIndex: index) ?? false + + + let button = UIButton(type: .custom) + button.tag = index + button.setTitle(text, for: .normal) + button.setAttributedTitle(attributedTitle, for: .normal) + button.setTitleColor(textColor, for: .normal) + button.setTitleColor(textSelectedColor, for: .selected) + button.setTitleColor(textSelectedColor, for: [.highlighted, .selected]) + button.setTitleColor(textDisabledColor, for: .disabled) + button.titleLabel?.font = textFont + button.layer.cornerRadius = tagCornerRadius + button.layer.masksToBounds = true + button.layer.borderWidth = tagBorderWidth + button.addTarget(self, action: #selector(handleButton(sender:)), for: .touchUpInside) + button.isEnabled = isEnble + button.titleLabel?.lineBreakMode = .byTruncatingTail + button.titleEdgeInsets = UIEdgeInsets(top: 0, left: textMargin, bottom: 0, right: textMargin) + + //修改选中状态 + button.isSelected = isSelected + + if isSelected { + selectedIndex = index + } + + self.addSubview(button) + self.buttonArr.append(button) + } + self.updateLayout() + + } + + + ///更新布局 + func updateLayout() { + var x = leftAndRightMargin + var y = topAndBottomMargin + let height = tagHeight + var width: CGFloat = 0 + + for button in buttonArr { + if tagWidth <= 0 { + if let string = button.currentTitle { + width = string.size(font: textFont).width + (textMargin * 2) + } else if let string = button.currentAttributedTitle { +// width = string.size(font: textFont).width + (textMargin * 2) + width = string.size().width + textMargin * 2 + } + + //限制宽度不能超出屏幕 + if layoutDirection == .vertical { + if width > self.width - (leftAndRightMargin * 2) { + width = self.width - (leftAndRightMargin * 2) + } + } + + //限制不能小于圆弧大小 + if width < tagCornerRadius * 2 { + width = tagCornerRadius * 2 + } + } else { + width = tagWidth + } + + if width < tagMinWidth { + width = tagMinWidth + } + + if layoutDirection == .vertical { + //判断是否需要换行 + if Float(x + width + leftAndRightMargin) > Float(self.width) { + x = leftAndRightMargin + y = y + height + tagVerticalGap + } + } + + + button.frame = CGRect(x: x, y: y, width: width, height: height) + + x = x + width + tagHorizontalGap + } + self.invalidateIntrinsicContentSize() + } + + + @objc func handleButton(sender: UIButton) { + self.delegate?.jx_tagView?(tagView: self, didSelectedTagAt: sender.tag) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateState() + } + + +// private func _setButtonBackgroundColor(button: UIButton, isSelected: Bool, isEnble: Bool) { +// if !isEnble { +// button.backgroundColor = tagDisabledBackgroundColor +// } else if isSelected { +// button.backgroundColor = tagSelectedBackgroundColor +// } else { +// button.backgroundColor = tagBackgroundColor +// } +// +// if isSelected { +// button.layer.borderColor = tagBorderSelectedColor?.cgColor +// } else { +// button.layer.borderColor = tagBorderColor?.cgColor +// } +// +// } + +} + + + +extension UIButton { +// private struct AssociatedKeys { +// static var borderColors: Int? +// } + + + + func jx_setBackgroundImage(_ image: UIImage?, for state: UIControl.State) { + self.setBackgroundImage(image, for: state) + if state == .selected { + self.setBackgroundImage(image, for: [state, .highlighted]) + } + } + + func jx_setImage(_ image: UIImage?, for state: UIControl.State) { + self.setImage(image, for: state) + if state == .selected { + self.setImage(image, for: [state, .highlighted]) + } + } + + func jx_setTitle(_ title: String?, for state: UIControl.State) { + self.setTitle(title, for: state) + if state == .selected { + self.setTitle(title, for: [state, .highlighted]) + } + } + + func jx_setTitleColor(_ color: UIColor?, for state: UIControl.State) { + self.setTitleColor(color, for: state) + if state == .selected { + self.setTitleColor(color, for: [state, .highlighted]) + } + } + +} +