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];
}
CodePudding user response:
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],
]];