0

In my app, there is one small section that displays lyrics to songs (PDFs loaded in a WKWebView). What I want to be able to do, is when you mirror that screen to an Apple TV, still control it with the iPhone/iPad, but have it be the full screen, optimized for a TV display. I have the following code set up in the Class for viewing the songs, but it keeps crashing with autoLayoutConstraint errors when I try loading it. Anyone have an idea what's going on?

Songs.h:
@interface Songs : UIViewController <UIGestureRecognizerDelegate>{
    IBOutlet WKWebView *savedweb;
    NSString *selectedCountry;
    IBOutlet UIActivityIndicatorView *activity;
    NSTimer *timer;
}

@property (nonatomic, retain) NSString *selectedSong;
@property (nonatomic, retain) UIActivityIndicatorView *activity;
@property (nonatomic, retain) UIWindow *secondWindow;
@end

Songs.m

- (void)viewDidLoad {
    if ([UIScreen screens].count > 1) {
        [self setUpSecondWindowForScreen:[UIScreen screens][1]];
      }

      NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
      [center addObserver:self selector:@selector(handleScreenDidConnectNotification:)
                     name:UIScreenDidConnectNotification object:nil];
      [center addObserver:self selector:@selector(handleScreenDidDisconnectNotification:)
                     name:UIScreenDidDisconnectNotification object:nil];
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
       NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *pdfPath = [[documentsDirectory stringByAppendingPathComponent:selectedSong] stringByAppendingString:@".pdf"];
   
        NSURL *url = [NSURL fileURLWithPath:pdfPath];
        NSLog(@"%@",url);
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        [savedweb loadRequest:request];
        UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleNavBar:)];
        [self.view addGestureRecognizer:gesture];
        gesture.delegate = self;
        [gesture release];
        timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:@selector(tick) userInfo:nil repeats:YES];
        
    
   
}
- (void)handleScreenDidConnectNotification:(NSNotification*)notification {
  if (!self.secondWindow) {
    [self setUpSecondWindowForScreen:[notification object]];
  }
}

- (void)handleScreenDidDisconnectNotification:(NSNotification*)notification {
  if (self.secondWindow) {
    self.secondWindow = nil;
  }
}
- (void)setUpSecondWindowForScreen:(UIScreen*)screen {
  self.secondWindow = [[UIWindow alloc] init];
  self.secondWindow.screen = screen;
  self.secondWindow.screen.overscanCompensation = UIScreenOverscanCompensationNone;

  UIViewController *viewController = [[UIViewController alloc] init];

    WKWebView *theSongView = [[WKWebView alloc] initWithFrame:viewController.view.frame];
    [theSongView setTranslatesAutoresizingMaskIntoConstraints:NO];
    theSongView.frame = CGRectMake(0, 0, viewController.view.bounds.size.width, viewController.view.bounds.size.height);
       [viewController.view addSubview:theSongView];
       
//This is where the error occurs
[viewController.view addConstraint:[NSLayoutConstraint constraintWithItem:theSongView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:viewController.view.safeAreaLayoutGuide.bottomAnchor attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
           [viewController.view addConstraint:[NSLayoutConstraint constraintWithItem:theSongView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:viewController.view.safeAreaLayoutGuide.topAnchor attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]];
           [viewController.view addConstraint:[NSLayoutConstraint constraintWithItem:theSongView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:viewController.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]];
           [viewController.view addConstraint:[NSLayoutConstraint constraintWithItem:theSongView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:viewController.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]];
        self.secondWindow.rootViewController = viewController;
    
        [self.secondWindow makeKeyAndVisible];
       
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
           NSString *documentsDirectory = [paths objectAtIndex:0];
        NSString *pdfPath = [[documentsDirectory stringByAppendingPathComponent:selectedSong] stringByAppendingString:@".pdf"];
       
            NSURL *url = [NSURL fileURLWithPath:pdfPath];
            NSLog(@"%@",url);
            NSURLRequest *request = [NSURLRequest requestWithURL:url];
            [theSongView loadRequest:request];
          
    }
user717452
  • 33
  • 14
  • 73
  • 149
  • What are the errors? – DonMag Aug 03 '22 at 20:30
  • @DonMag All I get is "ResolveConstraintArguments". It throws it on the line where I'm adding constraints on the secondWindow for the WKWebView.....my device is running beta of iOS 16, so I can't debug the greatest, am having to use TestFlight as I don't have the Xcode beta installed. – user717452 Aug 03 '22 at 20:33
  • A bit confusing... are you trying to constrain `theSongView` to all 4 sides of `viewController.view`? – DonMag Aug 03 '22 at 20:41
  • @DonMag Yes, just want it to be full screen, as this would more than likely be someone mirroring via AirPlay to a TV – user717452 Aug 03 '22 at 20:45

1 Answers1

1

The error message I get with your code includes:

Constraint items must each be a view or layout guide.

So, it's pretty clear where to look.

Formatting your first constraint for readability:

[viewController.view addConstraint:[
    NSLayoutConstraint constraintWithItem:theSongView
    attribute:NSLayoutAttributeBottom
    relatedBy:NSLayoutRelationEqual
    toItem:viewController.view.safeAreaLayoutGuide.bottomAnchor
    attribute:NSLayoutAttributeTop
    multiplier:1.0
    constant:0]];

You're trying to set the theSongView's Bottom constraint equal to a .bottomAnchor's Top constraint. Which is, of course, not what you want to do.

If you change that constraint (and fix the others) like this:

[viewController.view addConstraint:[
    NSLayoutConstraint constraintWithItem:theSongView
    attribute:NSLayoutAttributeBottom
    relatedBy:NSLayoutRelationEqual
    toItem:viewController.view.safeAreaLayoutGuide
    attribute:NSLayoutAttributeBottom
    multiplier:1.0
    constant:0]];

You should be on your way.

I would strongly suggest, though, that you start using the more modern syntax for layout constraints:

UILayoutGuide *safeG = [viewController.view safeAreaLayoutGuide];

[NSLayoutConstraint activateConstraints:@[
    [theSongView.topAnchor constraintEqualToAnchor:safeG.topAnchor],
    [theSongView.leadingAnchor constraintEqualToAnchor:safeG.leadingAnchor],
    [theSongView.trailingAnchor constraintEqualToAnchor:safeG.trailingAnchor],
    [theSongView.bottomAnchor constraintEqualToAnchor:safeG.bottomAnchor],
]];
DonMag
  • 69,424
  • 5
  • 50
  • 86
  • I tried it with the UILayout Guide, and it still crashes, giving me this: -`[NSLayoutConstraint _setActive:mutuallyExclusiveConstraints:]` – user717452 Aug 03 '22 at 21:32
  • UPDATE: Somehow had forgot to add the subview in which created an error, but fixed that and it is now working. Just have to do some tweaking now as it isn't quite fitting on the screen properly. – user717452 Aug 03 '22 at 21:44
  • @user717452 - did you replace ***all*** of your constraint code with the example I provided? – DonMag Aug 03 '22 at 21:45
  • Did you put that new constraint code ***after*** `[viewController.view addSubview:theSongView];`? – DonMag Aug 03 '22 at 21:46
  • Yea, the issue was I had accidentally erased the addSubview, so doing that took care of the problem. It adds it fine now, just is clipped at the bottom quite a bit. – user717452 Aug 03 '22 at 21:49
  • I'll add a new question for that issue, if you wanted to take a look at it. – user717452 Aug 03 '22 at 22:06
  • https://stackoverflow.com/questions/73228314/external-display-view-bounds-to-match-tv-resolution – user717452 Aug 03 '22 at 22:09