提问人:user717452 提问时间:7/31/2023 最后编辑:user717452 更新时间:8/8/2023 访问量:140
按字母导航 tvOS UITableView
Navigating tvOS UITableView By Letter of Alphabet
问:
这是 YouTube 上显示该问题的剪辑。视频
我有一个UITableView,它按字母顺序列出了位于应用程序中的文件。由于有超过 1500 个文件,我正在尝试实现在遥控器上单击右键时按字母表导航的功能。我遇到的问题是 TableView 确实移动到了正确的位置,但是我没有像往常一样突出显示,而是获得了较浅的突出显示颜色,并且单击选择什么也没做。如果我随后按下正常导航,它会将其向上移动到列表中的第 2 项。我做错了什么?
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.definesPresentationContext = YES; // know where you want UISearchController to be displayed
self.currentIndex = 0;
[self populateTheView];
}
- (void)populateTheView {
NSBundle *bundle = [NSBundle mainBundle];
self.title = @"Devo Songs";
self.files = [bundle pathsForResourcesOfType:@"pdf" inDirectory:@"WorshipSongs"];
NSString *documentsDirectoryPath = [self.files objectAtIndex:thepath.row];
self.filenames = [[documentsDirectoryPath lastPathComponent] stringByDeletingPathExtension];
NSMutableArray *names = [NSMutableArray arrayWithCapacity:[self.files count]];
for (NSString *path in self.files) {
[names addObject:[[path lastPathComponent] stringByDeletingPathExtension]];
}
//self.files = names;
self.files = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
NSLog(@"FILESLOAD%@", self.files);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder]; // Make the view controller the first responder to handle remote control events.
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
// Override pressesBegan method to detect right arrow key press event.
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
[super pressesEnded:presses withEvent:event];
for (UIPress *press in presses) {
if (press.type == UIPressTypeRightArrow) {
[self handleRightButtonPress];
}
}
}
- (void)handleRightButtonPress {
// Get the current name.
NSString *currentName = self.files[self.currentIndex];
// Get the current letter.
NSString *currentLetter = [currentName substringToIndex:1];
// Find the next index starting from the current index.
NSInteger nextIndex = self.currentIndex + 1;
while (nextIndex < self.files.count) {
NSString *nextName = self.files[nextIndex];
NSString *nextLetter = [nextName substringToIndex:1];
if (![nextLetter isEqualToString:currentLetter]) {
break;
}
nextIndex++;
}
// Check if we found a valid next index.
if (nextIndex < self.files.count) {
// Scroll the table view to the row corresponding to the next letter.
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:nextIndex inSection:0]
atScrollPosition:UITableViewScrollPositionTop animated:NO];
// Update the current index to the new index.
self.currentIndex = nextIndex;
// Deselect the current selected row (if any).
NSIndexPath *previousSelectedIndexPath = [self.tableView indexPathForSelectedRow];
if (previousSelectedIndexPath) {
[self.tableView deselectRowAtIndexPath:previousSelectedIndexPath animated:NO];
}
// Select the cell at the specified index.
NSIndexPath *indexPathToSelect = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];
[self.tableView selectRowAtIndexPath:indexPathToSelect animated:NO scrollPosition:UITableViewScrollPositionNone];
} else {
// If we have reached the end of the list, reset the index to the first element.
self.currentIndex = 0;
// Scroll back to the top of the table view.
[self.tableView setContentOffset:CGPointZero animated:NO];
// Deselect the current selected row (if any).
NSIndexPath *previousSelectedIndexPath = [self.tableView indexPathForSelectedRow];
if (previousSelectedIndexPath) {
[self.tableView deselectRowAtIndexPath:previousSelectedIndexPath animated:NO];
}
// Select the cell at the specified index.
NSIndexPath *indexPathToSelect = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];
[self.tableView selectRowAtIndexPath:indexPathToSelect animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
不确定是否需要,但该应用程序的主要场景是选项卡栏控制器。它总共有两个选项卡,左侧的选项卡在启动时显示的是 Split View,第二个是这里讨论的选项卡,它只是一个 ,并通过故事板中的 segue 呈现。UITableViewController
答:
好了,系好安全带,孩子!我们即将为您已经非常出色的代码添加一些魔力!
你看,与iOS相比,tvOS有点不同。它有自己的焦点模型,就像舞台上神奇的聚光灯,总是试图一次将一个界面元素(我们的演员)放在聚光灯下。现在,您在以编程方式滚动和选择单元格方面做得非常出色,但聚光灯......呃,注意力跟不上。但不要害怕,我们有一个技巧!它被称为焦点指南。
把这个焦点指南看作是我们戏剧的导演。这将帮助我们准确地告诉聚光灯下一步该去哪里。
首先,让我们在我们的方法中构思一个新的焦点指南,并为它铺上红地毯,以引导我们的聚光灯:viewDidLoad
self.dapperFocusGuide = [[UIFocusGuide alloc] init];
[self.view addLayoutGuide:self.dapperFocusGuide];
// Alright, red carpet time.
[self.dapperFocusGuide.topAnchor constraintEqualToAnchor:self.tableView.topAnchor].active = YES;
[self.dapperFocusGuide.leftAnchor constraintEqualToAnchor:self.tableView.leftAnchor].active = YES;
[self.dapperFocusGuide.widthAnchor constraintEqualToAnchor:self.tableView.widthAnchor].active = YES;
[self.dapperFocusGuide.heightAnchor constraintEqualToAnchor:self.tableView.heightAnchor].active = YES;
接下来,让我们告诉我们的导演,当戏剧开始时,哪位演员需要聚光灯:
self.dapperFocusGuide.preferredFocusEnvironments = @[self.tableView.visibleCells.firstObject];
现在,请抓紧你的帽子,因为我们即将教我们的导演一些新技巧!我们将告诉它如何在我们的方法中处理脚本中的更改:handleRightButtonPress
// Rest of your spectacular code...
// Select the cell at the specified index.
NSIndexPath *indexPathToSelect = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];
[self.tableView selectRowAtIndexPath:indexPathToSelect animated:NO scrollPosition:UITableViewScrollPositionNone];
// And now, the moment we've all been waiting for. Drumroll please...
// Let's tell our director to shift the spotlight!
UITableViewCell *cellToFocus = [self.tableView cellForRowAtIndexPath:indexPathToSelect];
self.dapperFocusGuide.preferredFocusEnvironments = @[cellToFocus];
现在,别忘了告诉编译器,我们都是这里的剧院人,然后导入:UIFocusGuide
这样一来,当您按下右键时,tableView 现在应该正确地聚焦在正确的单元格上。这就像代码和 UI 之间精心编排的舞蹈。现在继续,鞠躬!你赚到了!🎭👏🎉
这就是所有的东西......
#import <UIKit/UIFocusGuide.h>
@interface YourTableViewController ()
@property (nonatomic, strong) NSArray *files;
@property (nonatomic, strong) NSString *filenames;
@property (nonatomic, strong) UIFocusGuide *dapperFocusGuide;
@property (nonatomic, assign) NSInteger currentIndex;
@end
@implementation YourTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
// We're setting the stage!
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.definesPresentationContext = YES;
self.currentIndex = 0;
// Time to call the stars for a rehearsal.
[self populateTheView];
// Let's make room for our magical focus director.
self.dapperFocusGuide = [[UIFocusGuide alloc] init];
[self.view addLayoutGuide:self.dapperFocusGuide];
// Red carpet time!
[self.dapperFocusGuide.topAnchor constraintEqualToAnchor:self.tableView.topAnchor].active = YES;
[self.dapperFocusGuide.leftAnchor constraintEqualToAnchor:self.tableView.leftAnchor].active = YES;
[self.dapperFocusGuide.widthAnchor constraintEqualToAnchor:self.tableView.widthAnchor].active = YES;
[self.dapperFocusGuide.heightAnchor constraintEqualToAnchor:self.tableView.heightAnchor].active = YES;
}
- (void)populateTheView {
NSBundle *bundle = [NSBundle mainBundle];
self.title = @"Devo Songs";
self.files = [bundle pathsForResourcesOfType:@"pdf" inDirectory:@"WorshipSongs"];
NSString *documentsDirectoryPath = [self.files objectAtIndex:self.currentIndex];
self.filenames = [[documentsDirectoryPath lastPathComponent] stringByDeletingPathExtension];
NSMutableArray *names = [NSMutableArray arrayWithCapacity:[self.files count]];
for (NSString *path in self.files) {
[names addObject:[[path lastPathComponent] stringByDeletingPathExtension]];
}
self.files = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
NSLog(@"FILESLOAD%@", self.files);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Cue the music! The show has started.
[self becomeFirstResponder];
// Initial spotlight position.
self.dapperFocusGuide.preferredFocusEnvironments = @[self.tableView.visibleCells.firstObject];
}
- (BOOL)canBecomeFirstResponder {
return YES; // Of course we can! We're the star of the show!
}
// Here we keep an eye out for any new script changes (aka right arrow key press events).
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
[super pressesEnded:presses withEvent:event];
for (UIPress *press in presses) {
if (press.type == UIPressTypeRightArrow) {
[self handleRightButtonPress];
}
}
}
- (void)handleRightButtonPress {
// Your logic here to find the next index...
// Get the newly called star ready for the scene.
NSIndexPath *indexPathToSelect = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];
[self.tableView selectRowAtIndexPath:indexPathToSelect animated:NO scrollPosition:UITableViewScrollPositionNone];
// And now, the moment we've all been waiting for. Drumroll please...
// The spotlight moves!
UITableViewCell *cellToFocus = [self.tableView cellForRowAtIndexPath:indexPathToSelect];
self.dapperFocusGuide.preferredFocusEnvironments = @[cellToFocus];
}
// Here you would have your other UITableViewDelegate and UITableViewDataSource methods...
@end
评论
在平台上,您应该使用焦点而不是选择进行导航,因此只需用于通知焦点索引路径,例如:tvOS
indexPathForPreferredFocusedViewInTableView
UITableView
@interface ViewController () <UITableViewDataSource, UITableViewDelegate>
...
@property (nonatomic) NSIndexPath* focusedIndexPath;
@end
@implementation ViewController
...
- (void)focusRow:(NSInteger)row {
self.focusedIndexPath = [NSIndexPath indexPathForRow:row inSection:0];
[self.tableView setNeedsFocusUpdate];
}
#pragma mark - UITableViewDelegate
- (NSIndexPath *)indexPathForPreferredFocusedViewInTableView:(UITableView *)tableView {
return self.focusedIndexPath;
}
@end
评论