Search code examples
objective-cxcodetvos

Set TV Menu Button To Go Back One Screen


My Apple TV app lists a collection of PDFs, and clicking one draws it on the screen. For some reason, though, it exits the app completely when someone hits the < / Menu button on the Apple TV Remote. What is going on to cause it to do that?

Up first is the code where it pulls up the list of all the PDFs, followed by the code for displaying the PDF.

-(void)viewDidLoad {
    [super viewDidLoad];
 
    self.definesPresentationContext = YES;  // know where you want UISearchController to be displayed
    
}
- (void)viewWillAppear:(BOOL)animated {
    if (self.searchControllerWasActive) {
        self.searchController.active = self.searchControllerWasActive;
        _searchControllerWasActive = NO;
        
        if (self.searchControllerSearchFieldWasFirstResponder) {
            [self.searchController.searchBar becomeFirstResponder];
            _searchControllerSearchFieldWasFirstResponder = NO;
        }
    }
    NSBundle *bundle = [NSBundle mainBundle];
    
    self.files  = [bundle pathsForResourcesOfType:@"pdf" inDirectory:@"AIMPDF"];
    NSString *documentsDirectoryPath = [self.files objectAtIndex:thepath.row];
    
    self.title = @"Devo Songs";
    self.filenames = [[documentsDirectoryPath lastPathComponent] stringByDeletingPathExtension];
    NSLog(@"%@", filenames);
    
    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:)];

    
    self.tableView.backgroundColor = [UIColor whiteColor];
    self.parentViewController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"iphonebackground.png"]];
    [super viewDidLoad];
    
    
    [super viewWillAppear:animated];
    
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [searchBar resignFirstResponder];
}

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
    // update the filtered array based on the search text
    NSString *searchText = searchController.searchBar.text;
    NSMutableArray *searchResults2 = [self.files mutableCopy];
    
    // strip out all the leading and trailing spaces
    NSString *strippedString = [searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    
    // break up the search terms (separated by spaces)
    NSArray *searchItems = nil;
    if (strippedString.length > 0) {
        searchItems = [strippedString componentsSeparatedByString:@" "];
    }
    
    // build all the "AND" expressions for each value in the searchString
    //
    NSMutableArray *andMatchPredicates = [NSMutableArray array];
    
    for (NSString *searchString in searchItems) {
       
        NSPredicate *sPredicate =
        [NSPredicate predicateWithFormat:@"SELF contains[c] %@", searchString];
        [searchResults2 filterUsingPredicate:sPredicate];
        
        // at this OR predicate to our master AND predicate
       // NSCompoundPredicate *orMatchPredicates = [NSCompoundPredicate orPredicateWithSubpredicates:searchItemsPredicate];
        //[andMatchPredicates addObject:orMatchPredicates];
    }
    
    // match up the fields of the Product object
  //  NSCompoundPredicate *finalCompoundPredicate =
    //[NSCompoundPredicate andPredicateWithSubpredicates:andMatchPredicates];
    //searchResults2 = [[searchResults filteredArrayUsingPredicate:finalCompoundPredicate] mutableCopy];
    
    // hand over the filtered results to our search results table
    APLResultsTableController *tableController = (APLResultsTableController *)self.searchController.searchResultsController;
    tableController.filteredProducts = searchResults2;
    [tableController.tableView reloadData];
}

- (void)handleSearchForTerm:(NSString *)searchTerm
{
    [self setSavedSearchTerm:searchTerm];
    
    if ([self searchResults] == nil)
    {
        NSMutableArray *array = [[NSMutableArray alloc] init];
        [self setSearchResults:array];
        array = nil;
    }
    
    [[self searchResults] removeAllObjects];
    
    if ([[self savedSearchTerm] length] != 0)
    {
        for (NSString *currentString in [self files])
        {
            if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
            {
                [[self searchResults] addObject:currentString];
            }
        }
    }
}- (void)viewDidUnload
{
    [super viewDidUnload];
    
    // Save the state of the search UI so that it can be restored if the view is re-created.
    [self setSavedSearchTerm:[[[self searchDisplayController] searchBar] text]];
    
    [self setSearchResults:nil];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200
    
    if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
    {
        // The device is an iPad running iPhone 3.2 or later.
        return YES;
    }
    else
    {
        // The device is an iPhone or iPod touch.
        return YES;
    }
    
#else
    
    // iPhone simulator
    return YES;
    
#endif
}


#pragma mark -
#pragma mark Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
    return 1;
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    NSInteger rows;
    
        rows = [[self files] count];
    return rows;
}


// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *filename = [[[self.files objectAtIndex:indexPath.row] lastPathComponent] stringByDeletingPathExtension];
    NSInteger row = [indexPath row];
    NSString *contentForThisRow = nil;
    
   
        contentForThisRow = filename;
    
    static NSString *CellIdentifier = @"CellIdentifier";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    
    
           [[cell textLabel] setText:contentForThisRow];
        cell.textLabel.font = [UIFont fontWithName:@"Helvetica Neue" size:90];
        cell.textLabel.textColor = [UIColor blackColor];
        cell.backgroundColor = [UIColor lightGrayColor];
        return cell;
    
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
        
        return 80;
    }
    else {
        return 120;
    }
    
}




- (void)tableView:(UITableView *)tableView didUpdateFocusInContext:(UITableViewFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
    //this gives you the indexpath of the focused cell
    NSIndexPath *nextIndexPath = [context nextFocusedIndexPath];
    NSLog(@"Do Something");
}



#pragma mark -
#pragma mark Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    self.selectedCountry = (tableView == self.tableView) ?
    self.files[indexPath.row] : self.resultsTableController.filteredProducts[indexPath.row];

    [self performSegueWithIdentifier:@"ShowSong" sender:self];

    
    
    
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Make sure your segue name in storyboard is the same as this line
    if ([[segue identifier] isEqualToString:@"ShowSong"])
    {
       
        NSLog(@"Selecting %@", self.selectedCountry);
        FirstViewController* userViewController = [segue destinationViewController];
        userViewController.selectedCountry = self.selectedCountry;

        //if you need to pass data to the next controller do it here
    }
}

#pragma mark -
#pragma mark Memory management

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Relinquish ownership any cached data, images, etc. that aren't in use.
}



- (void)dealloc {
    }


@end

And now the code for opening:

@implementation FirstViewController
    - (CGPDFDocumentRef)openPDFLocal:(NSString *)pdfURL {
        NSURL* NSUrl = [NSURL fileURLWithPath:pdfURL];
        
        return [self openPDF:NSUrl];
    }
    
    - (CGPDFDocumentRef)openPDFURL:(NSString *)pdfURL {
        NSURL* NSUrl= [NSURL URLWithString:pdfURL];
        
        return [self openPDF:NSUrl];
    }
    
    - (CGPDFDocumentRef)openPDF:(NSURL*)NSUrl {
        CFURLRef url = (CFURLRef)CFBridgingRetain(NSUrl);
        
        CGPDFDocumentRef myDocument;
        myDocument = CGPDFDocumentCreateWithURL(url);
        if (myDocument == NULL) {
            NSLog(@"can't open %@", NSUrl);
            CFRelease (url);
            return nil;
        }
        CFRelease (url);
        
        if (CGPDFDocumentGetNumberOfPages(myDocument) == 0) {
            CGPDFDocumentRelease(myDocument);
            return nil;
        }
        
        return myDocument;
    }
    - (void)drawDocument:(CGPDFDocumentRef)pdfDocument
    {
        // Get the total number of pages for the whole PDF document
        int  totalPages= (int)CGPDFDocumentGetNumberOfPages(pdfDocument);
        self.pages = totalPages;
        NSMutableArray *pageImages = [[NSMutableArray alloc] init];
        
        // Iterate through the pages and add each page image to an array
        for (int i = 1; i <= totalPages; i++) {
            // Get the first page of the PDF document
            CGPDFPageRef page = CGPDFDocumentGetPage(pdfDocument, i);
            CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
            
            // Begin the image context with the page size
            // Also get the grapgics context that we will draw to
            UIGraphicsBeginImageContext(pageRect.size);
            CGContextRef context = UIGraphicsGetCurrentContext();
            
            // Rotate the page, so it displays correctly
            CGContextTranslateCTM(context, 0.0, pageRect.size.height);
            CGContextScaleCTM(context, 1.0, -1.0);
            CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, pageRect, 0, true));
            
            // Draw to the graphics context
            CGContextDrawPDFPage(context, page);
            
            // Get an image of the graphics context
            UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            [pageImages addObject:image];
        }
        // Set the image of the PDF to the current view
        [self addImagesToScrollView:pageImages];
    }
    
    -(void)addImagesToScrollView:(NSMutableArray*)imageArray {
        int heigth = 0;
        for (UIImage *image in imageArray) {
            UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
            imgView.frame=CGRectMake(200, heigth, 1520, 1080);
            [_scrollView addSubview:imgView];
            heigth += imgView.frame.size.height;
        }
    }
    -(void)viewDidLayoutSubviews {
        NSLog(@"%ld", (long)self.pages);
        
        _scrollView.contentSize = CGSizeMake(1920, 1080*self.pages);
        
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        NSLog(@"DOCUMENTS%@", self.selectedCountry);
        NSString *testing = [self.selectedCountry stringByAppendingString:@".pdf"];
        //This is passed in from a tableview after selecting the PDF needed.
        _scrollView.panGestureRecognizer.allowedTouchTypes = @[ @(UITouchTypeIndirect) ];
        NSString *Documents = [[NSBundle mainBundle] pathForResource:self.selectedCountry ofType:@"pdf" inDirectory:@"AIMPDF"];
        NSLog(@"OKOKOK%@", Documents);
        NSURL *url = [NSURL fileURLWithPath:Documents];
        self.view.backgroundColor = [UIColor blackColor];
        CGPDFDocumentRef pdfDocument = [self openPDF:url];
        [self drawDocument:pdfDocument];
    }
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end

Solution

  • In your case the searching view controller stays on the top level of your app that's why the app is closed by pressing Menu button instead of returning to your start screen. On tvOS you should not use UISearchController directly and call [self.searchController.searchBar becomeFirstResponder]; or self.searchController.active = YES; to present searching because it breaks a stacked view hierarchy that allows you to navigate upper by presented view controllers.

    On Apple TV, people typically navigate by moving through stacked screens of content. Each screen may present entry points to other screens, and provides a way — through the remote — to return to the previous screen or main menu. https://developer.apple.com/design/human-interface-guidelines/tvos/app-architecture/navigation/

    There is UISearchContainerViewController view controller that manages the presentation of search results in your interface and you can use it for searching:

    In tvOS, rather than push a UISearchController onto a navigation controller’s stack or use one as a child of another container view controller, embed an instance of this class and let it manage the presentation of the search controller’s content. https://developer.apple.com/documentation/uikit/uisearchcontainerviewcontroller

    For instance:

    UITableViewController* searchTableViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"SearchTableViewController"];
    UISearchController* searchController = [[UISearchController alloc] initWithSearchResultsController:searchTableViewController];
    searchController.delegate = self;
    searchController.searchResultsUpdater = self;
    searchController.searchBar.delegate = self;
    
    self.searchContainerViewController = [[UISearchContainerViewController alloc] initWithSearchController:searchController];
    
    // Show searching
    [self presentViewController:self.searchContainerViewController animated:YES completion:nil];