JXSearchViewController.m 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. //
  2. // JXSearchViewController.m
  3. // AICity
  4. //
  5. // Created by TogetherWatch on 2025-10-20.
  6. //
  7. #import "JXSearchViewController.h"
  8. #import "JXAPIService.h"
  9. #import "JXDrama.h"
  10. // Cell复用标识符
  11. static NSString * const kSearchResultCellIdentifier = @"SearchResultCell";
  12. static NSString * const kHotRecommendCellIdentifier = @"HotRecommendCell";
  13. @interface JXSearchViewController () <UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate>
  14. #pragma mark - UI组件
  15. // 顶部搜索栏
  16. @property (nonatomic, strong) UIView *searchBarContainer;
  17. @property (nonatomic, strong) UIButton *backButton;
  18. @property (nonatomic, strong) UITextField *searchTextField;
  19. @property (nonatomic, strong) UIButton *searchButton;
  20. // 搜索历史区域
  21. @property (nonatomic, strong) UIView *searchHistoryContainer;
  22. @property (nonatomic, strong) UILabel *historyTitleLabel;
  23. @property (nonatomic, strong) UIButton *clearHistoryButton;
  24. @property (nonatomic, strong) UIView *historyTagsContainer;
  25. // 热门推荐区域
  26. @property (nonatomic, strong) UIView *hotDiscoverContainer;
  27. @property (nonatomic, strong) UILabel *hotDiscoverTitleLabel;
  28. @property (nonatomic, strong) UIView *hotDiscoverListContainer;
  29. // 热门推荐列表(未搜索时显示)
  30. @property (nonatomic, strong) UIView *hotRecommendContainer;
  31. @property (nonatomic, strong) UITableView *hotRecommendTableView;
  32. // 搜索结果列表
  33. @property (nonatomic, strong) UIView *searchResultContainer;
  34. @property (nonatomic, strong) UITableView *searchResultTableView;
  35. @property (nonatomic, strong) UIView *emptyView;
  36. #pragma mark - 数据
  37. @property (nonatomic, copy) NSString *currentKeyword;
  38. @property (nonatomic, strong) NSMutableArray<NSString *> *searchHistory;
  39. @property (nonatomic, strong) NSArray<NSString *> *hotDiscover;
  40. @property (nonatomic, strong) NSMutableArray<JXDrama *> *hotRecommendList;
  41. @property (nonatomic, strong) NSMutableArray<JXDrama *> *searchResultList;
  42. #pragma mark - 状态
  43. @property (nonatomic, assign) BOOL isSearching;
  44. @property (nonatomic, assign) BOOL isLoadingHotRecommend;
  45. @property (nonatomic, assign) BOOL isLoadingSearchResult;
  46. @property (nonatomic, assign) NSInteger hotRecommendPage;
  47. @property (nonatomic, assign) NSInteger searchResultPage;
  48. @end
  49. @implementation JXSearchViewController
  50. #pragma mark - 初始化
  51. - (instancetype)initWithKeyword:(NSString *)keyword {
  52. self = [super init];
  53. if (self) {
  54. _currentKeyword = keyword;
  55. _searchHistory = [NSMutableArray array];
  56. _hotRecommendList = [NSMutableArray array];
  57. _searchResultList = [NSMutableArray array];
  58. _hotRecommendPage = 1;
  59. _searchResultPage = 1;
  60. }
  61. return self;
  62. }
  63. - (instancetype)init {
  64. return [self initWithKeyword:nil];
  65. }
  66. #pragma mark - 生命周期
  67. - (void)viewDidLoad {
  68. [super viewDidLoad];
  69. self.view.backgroundColor = [UIColor whiteColor];
  70. [self setupUI];
  71. [self loadSearchHistory];
  72. [self loadHotDiscover];
  73. [self loadHotRecommend];
  74. // 如果有初始关键词,执行搜索
  75. if (self.currentKeyword && self.currentKeyword.length > 0) {
  76. self.searchTextField.text = self.currentKeyword;
  77. [self performSearch:self.currentKeyword];
  78. }
  79. }
  80. #pragma mark - UI设置
  81. - (void)setupUI {
  82. // 搜索栏
  83. [self setupSearchBar];
  84. // 搜索历史
  85. [self setupSearchHistory];
  86. // 热门推荐(热搜榜)
  87. [self setupHotDiscover];
  88. // 热门推荐列表
  89. [self setupHotRecommend];
  90. // 搜索结果
  91. [self setupSearchResult];
  92. // 初始状态:显示热门推荐,隐藏搜索结果
  93. self.hotRecommendContainer.hidden = NO;
  94. self.searchResultContainer.hidden = YES;
  95. }
  96. - (void)setupSearchBar {
  97. self.searchBarContainer = [[UIView alloc] init];
  98. self.searchBarContainer.backgroundColor = [UIColor whiteColor];
  99. [self.view addSubview:self.searchBarContainer];
  100. // 返回按钮
  101. self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
  102. [self.backButton setImage:[UIImage systemImageNamed:@"chevron.left"] forState:UIControlStateNormal];
  103. self.backButton.tintColor = [UIColor blackColor];
  104. [self.backButton addTarget:self action:@selector(handleBack) forControlEvents:UIControlEventTouchUpInside];
  105. [self.searchBarContainer addSubview:self.backButton];
  106. // 搜索输入框
  107. self.searchTextField = [[UITextField alloc] init];
  108. self.searchTextField.placeholder = @"搜索短剧";
  109. self.searchTextField.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
  110. self.searchTextField.layer.cornerRadius = 20;
  111. self.searchTextField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 12, 0)];
  112. self.searchTextField.leftViewMode = UITextFieldViewModeAlways;
  113. self.searchTextField.returnKeyType = UIReturnKeySearch;
  114. self.searchTextField.delegate = self;
  115. [self.searchTextField addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
  116. [self.searchBarContainer addSubview:self.searchTextField];
  117. // 搜索按钮
  118. self.searchButton = [UIButton buttonWithType:UIButtonTypeSystem];
  119. [self.searchButton setTitle:@"搜索" forState:UIControlStateNormal];
  120. [self.searchButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  121. self.searchButton.backgroundColor = [UIColor systemBlueColor];
  122. self.searchButton.layer.cornerRadius = 20;
  123. [self.searchButton addTarget:self action:@selector(handleSearch) forControlEvents:UIControlEventTouchUpInside];
  124. [self.searchBarContainer addSubview:self.searchButton];
  125. // 约束
  126. self.searchBarContainer.translatesAutoresizingMaskIntoConstraints = NO;
  127. self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
  128. self.searchTextField.translatesAutoresizingMaskIntoConstraints = NO;
  129. self.searchButton.translatesAutoresizingMaskIntoConstraints = NO;
  130. [NSLayoutConstraint activateConstraints:@[
  131. [self.searchBarContainer.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
  132. [self.searchBarContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
  133. [self.searchBarContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
  134. [self.searchBarContainer.heightAnchor constraintEqualToConstant:60],
  135. [self.backButton.leadingAnchor constraintEqualToAnchor:self.searchBarContainer.leadingAnchor constant:12],
  136. [self.backButton.centerYAnchor constraintEqualToAnchor:self.searchBarContainer.centerYAnchor],
  137. [self.backButton.widthAnchor constraintEqualToConstant:40],
  138. [self.backButton.heightAnchor constraintEqualToConstant:40],
  139. [self.searchTextField.leadingAnchor constraintEqualToAnchor:self.backButton.trailingAnchor constant:8],
  140. [self.searchTextField.centerYAnchor constraintEqualToAnchor:self.searchBarContainer.centerYAnchor],
  141. [self.searchTextField.heightAnchor constraintEqualToConstant:40],
  142. [self.searchButton.leadingAnchor constraintEqualToAnchor:self.searchTextField.trailingAnchor constant:8],
  143. [self.searchButton.trailingAnchor constraintEqualToAnchor:self.searchBarContainer.trailingAnchor constant:-12],
  144. [self.searchButton.centerYAnchor constraintEqualToAnchor:self.searchBarContainer.centerYAnchor],
  145. [self.searchButton.widthAnchor constraintEqualToConstant:60],
  146. [self.searchButton.heightAnchor constraintEqualToConstant:40]
  147. ]];
  148. }
  149. - (void)setupSearchHistory {
  150. self.searchHistoryContainer = [[UIView alloc] init];
  151. self.searchHistoryContainer.backgroundColor = [UIColor whiteColor];
  152. [self.view addSubview:self.searchHistoryContainer];
  153. // 标题
  154. self.historyTitleLabel = [[UILabel alloc] init];
  155. self.historyTitleLabel.text = @"搜索历史";
  156. self.historyTitleLabel.font = [UIFont boldSystemFontOfSize:16];
  157. [self.searchHistoryContainer addSubview:self.historyTitleLabel];
  158. // 清除按钮
  159. self.clearHistoryButton = [UIButton buttonWithType:UIButtonTypeSystem];
  160. [self.clearHistoryButton setImage:[UIImage systemImageNamed:@"trash"] forState:UIControlStateNormal];
  161. self.clearHistoryButton.tintColor = [UIColor grayColor];
  162. [self.clearHistoryButton addTarget:self action:@selector(handleClearHistory) forControlEvents:UIControlEventTouchUpInside];
  163. [self.searchHistoryContainer addSubview:self.clearHistoryButton];
  164. // 历史标签容器
  165. self.historyTagsContainer = [[UIView alloc] init];
  166. [self.searchHistoryContainer addSubview:self.historyTagsContainer];
  167. // 约束
  168. self.searchHistoryContainer.translatesAutoresizingMaskIntoConstraints = NO;
  169. self.historyTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  170. self.clearHistoryButton.translatesAutoresizingMaskIntoConstraints = NO;
  171. self.historyTagsContainer.translatesAutoresizingMaskIntoConstraints = NO;
  172. [NSLayoutConstraint activateConstraints:@[
  173. [self.searchHistoryContainer.topAnchor constraintEqualToAnchor:self.searchBarContainer.bottomAnchor constant:16],
  174. [self.searchHistoryContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
  175. [self.searchHistoryContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
  176. [self.searchHistoryContainer.heightAnchor constraintGreaterThanOrEqualToConstant:60],
  177. [self.historyTitleLabel.topAnchor constraintEqualToAnchor:self.searchHistoryContainer.topAnchor constant:12],
  178. [self.historyTitleLabel.leadingAnchor constraintEqualToAnchor:self.searchHistoryContainer.leadingAnchor constant:16],
  179. [self.clearHistoryButton.topAnchor constraintEqualToAnchor:self.searchHistoryContainer.topAnchor constant:12],
  180. [self.clearHistoryButton.trailingAnchor constraintEqualToAnchor:self.searchHistoryContainer.trailingAnchor constant:-16],
  181. [self.clearHistoryButton.widthAnchor constraintEqualToConstant:30],
  182. [self.clearHistoryButton.heightAnchor constraintEqualToConstant:30],
  183. [self.historyTagsContainer.topAnchor constraintEqualToAnchor:self.historyTitleLabel.bottomAnchor constant:12],
  184. [self.historyTagsContainer.leadingAnchor constraintEqualToAnchor:self.searchHistoryContainer.leadingAnchor constant:16],
  185. [self.historyTagsContainer.trailingAnchor constraintEqualToAnchor:self.searchHistoryContainer.trailingAnchor constant:-16],
  186. [self.historyTagsContainer.bottomAnchor constraintEqualToAnchor:self.searchHistoryContainer.bottomAnchor constant:-12]
  187. ]];
  188. }
  189. - (void)setupHotDiscover {
  190. self.hotDiscoverContainer = [[UIView alloc] init];
  191. self.hotDiscoverContainer.backgroundColor = [UIColor whiteColor];
  192. [self.view addSubview:self.hotDiscoverContainer];
  193. // 标题
  194. self.hotDiscoverTitleLabel = [[UILabel alloc] init];
  195. self.hotDiscoverTitleLabel.text = @"热门搜索";
  196. self.hotDiscoverTitleLabel.font = [UIFont boldSystemFontOfSize:16];
  197. [self.hotDiscoverContainer addSubview:self.hotDiscoverTitleLabel];
  198. // 热搜列表容器
  199. self.hotDiscoverListContainer = [[UIView alloc] init];
  200. [self.hotDiscoverContainer addSubview:self.hotDiscoverListContainer];
  201. // 约束
  202. self.hotDiscoverContainer.translatesAutoresizingMaskIntoConstraints = NO;
  203. self.hotDiscoverTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  204. self.hotDiscoverListContainer.translatesAutoresizingMaskIntoConstraints = NO;
  205. [NSLayoutConstraint activateConstraints:@[
  206. [self.hotDiscoverContainer.topAnchor constraintEqualToAnchor:self.searchHistoryContainer.bottomAnchor constant:16],
  207. [self.hotDiscoverContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
  208. [self.hotDiscoverContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
  209. [self.hotDiscoverContainer.heightAnchor constraintGreaterThanOrEqualToConstant:200],
  210. [self.hotDiscoverTitleLabel.topAnchor constraintEqualToAnchor:self.hotDiscoverContainer.topAnchor constant:12],
  211. [self.hotDiscoverTitleLabel.leadingAnchor constraintEqualToAnchor:self.hotDiscoverContainer.leadingAnchor constant:16],
  212. [self.hotDiscoverListContainer.topAnchor constraintEqualToAnchor:self.hotDiscoverTitleLabel.bottomAnchor constant:12],
  213. [self.hotDiscoverListContainer.leadingAnchor constraintEqualToAnchor:self.hotDiscoverContainer.leadingAnchor constant:16],
  214. [self.hotDiscoverListContainer.trailingAnchor constraintEqualToAnchor:self.hotDiscoverContainer.trailingAnchor constant:-16],
  215. [self.hotDiscoverListContainer.bottomAnchor constraintEqualToAnchor:self.hotDiscoverContainer.bottomAnchor constant:-12]
  216. ]];
  217. }
  218. - (void)setupHotRecommend {
  219. self.hotRecommendContainer = [[UIView alloc] init];
  220. self.hotRecommendContainer.backgroundColor = [UIColor whiteColor];
  221. [self.view addSubview:self.hotRecommendContainer];
  222. self.hotRecommendTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
  223. self.hotRecommendTableView.delegate = self;
  224. self.hotRecommendTableView.dataSource = self;
  225. self.hotRecommendTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  226. [self.hotRecommendTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kHotRecommendCellIdentifier];
  227. [self.hotRecommendContainer addSubview:self.hotRecommendTableView];
  228. // 约束
  229. self.hotRecommendContainer.translatesAutoresizingMaskIntoConstraints = NO;
  230. self.hotRecommendTableView.translatesAutoresizingMaskIntoConstraints = NO;
  231. [NSLayoutConstraint activateConstraints:@[
  232. [self.hotRecommendContainer.topAnchor constraintEqualToAnchor:self.hotDiscoverContainer.bottomAnchor constant:16],
  233. [self.hotRecommendContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
  234. [self.hotRecommendContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
  235. [self.hotRecommendContainer.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
  236. [self.hotRecommendTableView.topAnchor constraintEqualToAnchor:self.hotRecommendContainer.topAnchor],
  237. [self.hotRecommendTableView.leadingAnchor constraintEqualToAnchor:self.hotRecommendContainer.leadingAnchor],
  238. [self.hotRecommendTableView.trailingAnchor constraintEqualToAnchor:self.hotRecommendContainer.trailingAnchor],
  239. [self.hotRecommendTableView.bottomAnchor constraintEqualToAnchor:self.hotRecommendContainer.bottomAnchor]
  240. ]];
  241. }
  242. - (void)setupSearchResult {
  243. self.searchResultContainer = [[UIView alloc] init];
  244. self.searchResultContainer.backgroundColor = [UIColor whiteColor];
  245. [self.view addSubview:self.searchResultContainer];
  246. self.searchResultTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
  247. self.searchResultTableView.delegate = self;
  248. self.searchResultTableView.dataSource = self;
  249. self.searchResultTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  250. [self.searchResultTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kSearchResultCellIdentifier];
  251. [self.searchResultContainer addSubview:self.searchResultTableView];
  252. // 空视图
  253. self.emptyView = [[UIView alloc] init];
  254. self.emptyView.hidden = YES;
  255. [self.searchResultContainer addSubview:self.emptyView];
  256. UILabel *emptyLabel = [[UILabel alloc] init];
  257. emptyLabel.text = @"搜索结果为空\n换一个搜索词试试吧~";
  258. emptyLabel.textColor = [UIColor grayColor];
  259. emptyLabel.font = [UIFont systemFontOfSize:14];
  260. emptyLabel.textAlignment = NSTextAlignmentCenter;
  261. emptyLabel.numberOfLines = 0;
  262. [self.emptyView addSubview:emptyLabel];
  263. // 约束
  264. self.searchResultContainer.translatesAutoresizingMaskIntoConstraints = NO;
  265. self.searchResultTableView.translatesAutoresizingMaskIntoConstraints = NO;
  266. self.emptyView.translatesAutoresizingMaskIntoConstraints = NO;
  267. emptyLabel.translatesAutoresizingMaskIntoConstraints = NO;
  268. [NSLayoutConstraint activateConstraints:@[
  269. [self.searchResultContainer.topAnchor constraintEqualToAnchor:self.searchBarContainer.bottomAnchor],
  270. [self.searchResultContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
  271. [self.searchResultContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
  272. [self.searchResultContainer.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
  273. [self.searchResultTableView.topAnchor constraintEqualToAnchor:self.searchResultContainer.topAnchor],
  274. [self.searchResultTableView.leadingAnchor constraintEqualToAnchor:self.searchResultContainer.leadingAnchor],
  275. [self.searchResultTableView.trailingAnchor constraintEqualToAnchor:self.searchResultContainer.trailingAnchor],
  276. [self.searchResultTableView.bottomAnchor constraintEqualToAnchor:self.searchResultContainer.bottomAnchor],
  277. [self.emptyView.centerXAnchor constraintEqualToAnchor:self.searchResultContainer.centerXAnchor],
  278. [self.emptyView.centerYAnchor constraintEqualToAnchor:self.searchResultContainer.centerYAnchor],
  279. [self.emptyView.widthAnchor constraintEqualToConstant:200],
  280. [self.emptyView.heightAnchor constraintEqualToConstant:100],
  281. [emptyLabel.centerXAnchor constraintEqualToAnchor:self.emptyView.centerXAnchor],
  282. [emptyLabel.centerYAnchor constraintEqualToAnchor:self.emptyView.centerYAnchor]
  283. ]];
  284. }
  285. #pragma mark - 数据加载
  286. - (void)loadSearchHistory {
  287. // TODO: 从本地存储加载搜索历史
  288. // 暂时使用示例数据
  289. self.searchHistory = [@[@"热门短剧", @"爱情", @"悬疑"] mutableCopy];
  290. [self updateSearchHistoryUI];
  291. }
  292. - (void)saveSearchHistory:(NSString *)keyword {
  293. if (!keyword || keyword.length == 0) {
  294. return;
  295. }
  296. // 移除重复项
  297. [self.searchHistory removeObject:keyword];
  298. // 插入到最前面
  299. [self.searchHistory insertObject:keyword atIndex:0];
  300. // 最多保存10条
  301. if (self.searchHistory.count > 10) {
  302. [self.searchHistory removeLastObject];
  303. }
  304. // TODO: 保存到本地存储
  305. [self updateSearchHistoryUI];
  306. }
  307. - (void)loadHotDiscover {
  308. // TODO: 调用API获取热搜榜
  309. // 暂时使用示例数据
  310. self.hotDiscover = @[@"霸道总裁爱上我", @"穿越时空", @"都市言情", @"甜宠", @"逆袭", @"复仇", @"重生", @"古装"];
  311. [self updateHotDiscoverUI];
  312. }
  313. - (void)loadHotRecommend {
  314. if (self.isLoadingHotRecommend) {
  315. return;
  316. }
  317. self.isLoadingHotRecommend = YES;
  318. [[JXAPIService sharedService] getDramaListWithCategoryId:nil
  319. page:self.hotRecommendPage
  320. pageSize:20
  321. success:^(id response) {
  322. self.isLoadingHotRecommend = NO;
  323. NSDictionary *data = response[@"data"];
  324. NSArray *list = data[@"list"];
  325. NSMutableArray *models = [NSMutableArray array];
  326. for (NSDictionary *dict in list) {
  327. JXDrama *drama = [[JXDrama alloc] initWithDictionary:dict];
  328. [models addObject:drama];
  329. }
  330. [self.hotRecommendList addObjectsFromArray:models];
  331. [self.hotRecommendTableView reloadData];
  332. } failure:^(NSError *error) {
  333. self.isLoadingHotRecommend = NO;
  334. NSLog(@"[JXSearch] 加载热门推荐失败: %@", error.localizedDescription);
  335. }];
  336. }
  337. - (void)performSearch:(NSString *)keyword {
  338. if (!keyword || keyword.length == 0) {
  339. return;
  340. }
  341. self.isSearching = YES;
  342. self.currentKeyword = keyword;
  343. self.searchResultPage = 1;
  344. [self.searchResultList removeAllObjects];
  345. // 保存搜索历史
  346. [self saveSearchHistory:keyword];
  347. // 切换UI
  348. self.hotRecommendContainer.hidden = YES;
  349. self.searchResultContainer.hidden = NO;
  350. self.searchHistoryContainer.hidden = YES;
  351. self.hotDiscoverContainer.hidden = YES;
  352. // 执行搜索
  353. [self loadSearchResult];
  354. }
  355. - (void)loadSearchResult {
  356. if (self.isLoadingSearchResult) {
  357. return;
  358. }
  359. self.isLoadingSearchResult = YES;
  360. [[JXAPIService sharedService] searchDramasWithKeyword:self.currentKeyword
  361. page:self.searchResultPage
  362. pageSize:20
  363. success:^(id response) {
  364. self.isLoadingSearchResult = NO;
  365. NSDictionary *data = response[@"data"];
  366. NSArray *list = data[@"list"];
  367. NSMutableArray *models = [NSMutableArray array];
  368. for (NSDictionary *dict in list) {
  369. JXDrama *drama = [[JXDrama alloc] initWithDictionary:dict];
  370. [models addObject:drama];
  371. }
  372. [self.searchResultList addObjectsFromArray:models];
  373. [self.searchResultTableView reloadData];
  374. // 显示/隐藏空视图
  375. self.emptyView.hidden = (self.searchResultList.count > 0);
  376. } failure:^(NSError *error) {
  377. self.isLoadingSearchResult = NO;
  378. NSLog(@"[JXSearch] 搜索失败: %@", error.localizedDescription);
  379. }];
  380. }
  381. #pragma mark - UI更新
  382. - (void)updateSearchHistoryUI {
  383. // 清空现有视图
  384. for (UIView *subview in self.historyTagsContainer.subviews) {
  385. [subview removeFromSuperview];
  386. }
  387. if (self.searchHistory.count == 0) {
  388. self.searchHistoryContainer.hidden = YES;
  389. return;
  390. }
  391. self.searchHistoryContainer.hidden = NO;
  392. // 创建历史标签
  393. CGFloat xOffset = 0;
  394. CGFloat yOffset = 0;
  395. CGFloat maxWidth = self.view.bounds.size.width - 32;
  396. for (NSString *keyword in self.searchHistory) {
  397. UIButton *tagButton = [self createTagButton:keyword];
  398. CGSize size = [tagButton sizeThatFits:CGSizeMake(maxWidth, 36)];
  399. if (xOffset + size.width > maxWidth) {
  400. xOffset = 0;
  401. yOffset += 44;
  402. }
  403. tagButton.frame = CGRectMake(xOffset, yOffset, size.width, 36);
  404. [self.historyTagsContainer addSubview:tagButton];
  405. xOffset += size.width + 8;
  406. }
  407. }
  408. - (void)updateHotDiscoverUI {
  409. // 清空现有视图
  410. for (UIView *subview in self.hotDiscoverListContainer.subviews) {
  411. [subview removeFromSuperview];
  412. }
  413. if (!self.hotDiscover || self.hotDiscover.count == 0) {
  414. return;
  415. }
  416. // 创建热搜榜列表
  417. CGFloat yOffset = 0;
  418. for (NSInteger i = 0; i < self.hotDiscover.count; i++) {
  419. NSString *keyword = self.hotDiscover[i];
  420. UIView *itemView = [self createHotDiscoverItem:keyword index:i];
  421. itemView.frame = CGRectMake(0, yOffset, self.view.bounds.size.width - 32, 44);
  422. [self.hotDiscoverListContainer addSubview:itemView];
  423. yOffset += 44;
  424. }
  425. }
  426. - (UIButton *)createTagButton:(NSString *)title {
  427. UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
  428. [button setTitle:title forState:UIControlStateNormal];
  429. [button setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
  430. button.titleLabel.font = [UIFont systemFontOfSize:14];
  431. button.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
  432. button.layer.cornerRadius = 18;
  433. button.contentEdgeInsets = UIEdgeInsetsMake(8, 16, 8, 16);
  434. [button addTarget:self action:@selector(handleHistoryTagTap:) forControlEvents:UIControlEventTouchUpInside];
  435. return button;
  436. }
  437. - (UIView *)createHotDiscoverItem:(NSString *)keyword index:(NSInteger)index {
  438. UIView *itemView = [[UIView alloc] init];
  439. itemView.userInteractionEnabled = YES;
  440. UILabel *indexLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 30, 44)];
  441. indexLabel.text = [NSString stringWithFormat:@"%ld", (long)(index + 1)];
  442. indexLabel.font = [UIFont boldSystemFontOfSize:16];
  443. indexLabel.textColor = (index < 3) ? [UIColor systemRedColor] : [UIColor grayColor];
  444. indexLabel.textAlignment = NSTextAlignmentCenter;
  445. [itemView addSubview:indexLabel];
  446. UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 0, self.view.bounds.size.width - 72, 44)];
  447. titleLabel.text = keyword;
  448. titleLabel.font = [UIFont systemFontOfSize:15];
  449. titleLabel.textColor = [UIColor blackColor];
  450. [itemView addSubview:titleLabel];
  451. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleHotDiscoverTap:)];
  452. [itemView addGestureRecognizer:tap];
  453. itemView.tag = index;
  454. return itemView;
  455. }
  456. #pragma mark - 事件处理
  457. - (void)handleBack {
  458. [self.navigationController popViewControllerAnimated:YES];
  459. }
  460. - (void)handleSearch {
  461. NSString *keyword = [self.searchTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  462. [self performSearch:keyword];
  463. [self.searchTextField resignFirstResponder];
  464. }
  465. - (void)handleClearHistory {
  466. [self.searchHistory removeAllObjects];
  467. [self updateSearchHistoryUI];
  468. // TODO: 清除本地存储
  469. }
  470. - (void)handleHistoryTagTap:(UIButton *)sender {
  471. NSString *keyword = sender.titleLabel.text;
  472. self.searchTextField.text = keyword;
  473. [self performSearch:keyword];
  474. }
  475. - (void)handleHotDiscoverTap:(UITapGestureRecognizer *)gesture {
  476. NSInteger index = gesture.view.tag;
  477. if (index < self.hotDiscover.count) {
  478. NSString *keyword = self.hotDiscover[index];
  479. self.searchTextField.text = keyword;
  480. [self performSearch:keyword];
  481. }
  482. }
  483. - (void)textFieldDidChange {
  484. NSString *text = self.searchTextField.text;
  485. if (text.length == 0 && self.isSearching) {
  486. // 清空搜索,返回热门推荐
  487. self.isSearching = NO;
  488. self.hotRecommendContainer.hidden = NO;
  489. self.searchResultContainer.hidden = YES;
  490. self.searchHistoryContainer.hidden = NO;
  491. self.hotDiscoverContainer.hidden = NO;
  492. }
  493. }
  494. #pragma mark - UITextFieldDelegate
  495. - (BOOL)textFieldShouldReturn:(UITextField *)textField {
  496. [self handleSearch];
  497. return YES;
  498. }
  499. #pragma mark - UITableViewDataSource
  500. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  501. if (tableView == self.hotRecommendTableView) {
  502. return self.hotRecommendList.count;
  503. } else {
  504. return self.searchResultList.count;
  505. }
  506. }
  507. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  508. if (tableView == self.hotRecommendTableView) {
  509. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kHotRecommendCellIdentifier forIndexPath:indexPath];
  510. JXDrama *drama = self.hotRecommendList[indexPath.row];
  511. cell.textLabel.text = drama.title;
  512. return cell;
  513. } else {
  514. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kSearchResultCellIdentifier forIndexPath:indexPath];
  515. JXDrama *drama = self.searchResultList[indexPath.row];
  516. cell.textLabel.text = drama.title;
  517. return cell;
  518. }
  519. }
  520. #pragma mark - UITableViewDelegate
  521. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  522. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  523. JXDrama *drama;
  524. if (tableView == self.hotRecommendTableView) {
  525. drama = self.hotRecommendList[indexPath.row];
  526. } else {
  527. drama = self.searchResultList[indexPath.row];
  528. }
  529. NSLog(@"[JXSearch] 点击短剧: %@", drama.title);
  530. // TODO: 跳转到详情页或播放页
  531. }
  532. @end