I am attempting to scale a youtube video so that it is not distorted, but also fills the view with no black bars. I am ok with the video having cropped edges to accomplish this, so it would seem MPMovieScalingModeAspectFill is my best option. Also, this view is contained in another view.
However, the project I am doing requires all UI to be configured using constraints in-code. I can not use nib files or storyboard. I am finding it hard to get the edges to actually crop (it shows the video blown up to fit the top and bottom of the view with the horizontal part spilling out of my view) , and I assume this might have to do with my use of constraints versus using CGRect to define an actual frame.
My specific questions include:
Is it possible to get MPMovieScalingModeAspectFill to crop my videowhile defining its UI with constraints?
If it is possible, what am I doing wrong in my code?
Below is an example of my .m file with parts of the method that creates the video player view:
- (void) createMediaPlayer: (NSURL *)videoURL {
YouTubeVideo *selectedVideo = [youTubeCollection.items objectAtIndex:youTubeCollection.currentItem];
moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:selectedVideo.contentsURL]];
[moviePlayerController setContentURL:videoURL];
[moviePlayerController setShouldAutoplay:YES];
[moviePlayerController setRepeatMode:MPMovieRepeatModeNone];
[moviePlayerController setFullscreen:NO];
[moviePlayerController setControlStyle:MPMovieControlStyleNone];
[moviePlayerController setScalingMode:MPMovieScalingModeAspectFill];
playingVideo = YES;
moviePlayerController.view.translatesAutoresizingMaskIntoConstraints = NO;
[videoPlayerView addSubview:moviePlayerController.view];
[videoPlayerView sendSubviewToBack:videoPlayerView];
// Configure Constraints
[videoPlayerView addConstraint:[NSLayoutConstraint constraintWithItem:moviePlayerController.view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:videoPlayerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0]];
[videoPlayerView addConstraint:[NSLayoutConstraint constraintWithItem:moviePlayerController.view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:videoPlayerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0]];
[videoPlayerView addConstraint:[NSLayoutConstraint constraintWithItem:moviePlayerController.view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:videoPlayerView
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0]];
[videoPlayerView addConstraint:[NSLayoutConstraint constraintWithItem:moviePlayerController.view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:videoPlayerView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0]];
[moviePlayerController prepareToPlay];
[moviePlayerController play];
}
Thanks for both question and answer. But the following code actually worked for me:
moviePlayer = [[MPMoviePlayerController alloc] init];
[moviePlayer.view setFrame: _viewPlayer.bounds];
[moviePlayer.view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[_viewPlayer addSubview:moviePlayer.view];
.
. /* set constraints */
.
[moviePlayer setScalingMode:MPMovieScalingModeAspectFill];
The answer was rather simple, I needed to set my views "clipToBounds" to true while also incorporating setScalingMode:MPMovieScalingModeAspectFill with my movie player :
videoPlayerView.clipsToBounds = YES;
[moviePlayerController setScalingMode:MPMovieScalingModeAspectFill];
Related
I'm developing a custom keyboard and i want to use only a UICollectionView as content. The CollectionView should fill (center x and y, width and height available space) of the keyboardview.
My problem is that if i rotate the device the collection view won't be resized.
I tried layout constraints, resizing the view on viewWillTransitionToSize but it does not work. Can anyone give me a hint or show me a way to get this done?
Autolayout should set translatesAutoresizingMaskIntoConstraints to NO before adding Constraint. The code below can make a UICollectionView autolayout with equal edges.
- (void)viewDidLoad {
[super viewDidLoad];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collectionView.translatesAutoresizingMaskIntoConstraints = NO;
collectionView.backgroundColor = [UIColor redColor];
[self.view addSubview:collectionView];
//Adding layout constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:collectionView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:collectionView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:collectionView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:collectionView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
}
In addition, I recommend you to use Masonry to build autolayout. In your case, you just need to run the code below to make constraints.
[collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
By the way, autolayout will use more memory then calculating frame yourself. As iOS Keyboard extension has a very harsh memory usage limit (around 40mb depends on your device). If your keyboard would use many memory on process other things, I suggest you don't use autolayout.
When I first load the web view its frame is correct but when I rotate the device, its width is not update.
For instance, if the view was portrait and I rotate it in landscape the web view frame is not cover the whole view.
Loading web view
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadWebView];
}
-(void) loadWebView {
UIWebView *webView;
webView = [[UIWebView alloc] initWithFrame: self.view.frame];
NSString *htmlString = [NSString stringWithFormat:#"%#%#%#",html_header_with_files,DetailsHtml,HTML_FOOTER];
[webView loadHTMLString:htmlString baseURL:nil];
[self.view addSubview:webView];
}
First try:
I added a notification to realize the rotation
- (void) orientationChanged:(NSNotification *)note
{
[self.view layoutIfNeeded];
}
Above code did not solve the problem.
Second try
- (void) orientationChanged:(NSNotification *)note
{
[self loadWebView];
}
Above code did not solve the problem.
The view has to be added with auto layout so that it gets laid out properly in all devices with both orientation.
You need to create an extension of UIView and add the below method to make it a reusable code. You can also add the method in the same class if you are not going to use this method anywhere else.
- (void)addSubView:(UIView *)subView belowView:(UIView *)belowView inSuperView:(UIView *)superView {
[superView addSubview:subView];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]];
if (nil == belowView) {
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
} else {
[superView addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:belowView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]];
}
}
Then you need to call the above method like
[self addSubView:webView belowView:nil inSuperView:self.view];
Note:
To know more about auto-layout you can follow the tutorial
https://www.raywenderlich.com/443-auto-layout-tutorial-in-ios-11-getting-started
Also its better to start writing your app with swift rather than continuing objective-c even if some of your codebase is already in objective-c. A tutorial to make obj-c and swift inter operability can be seen in below link
https://medium.com/ios-os-x-development/swift-and-objective-c-interoperability-2add8e6d6887
I just want the WKWebView to be pinned to all sides of the self.view, so that it will always be stretched as far as possible no matter rotation. Using the following code, it will fill the view for whatever the initial rotation is, but after rotating, it simply all disappears:
-(void)viewWillAppear:(BOOL)animated {
[super viewDidLoad];
self.title = #"Worship Slides";
self.productURL = #"http://www.316apps.com/Fritch/worship.key";
NSURL *url = [NSURL URLWithString:self.productURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0];
_theWorship = [[WKWebView alloc] initWithFrame:self.view.frame];
[_theWorship setTranslatesAutoresizingMaskIntoConstraints:NO];
[_theWorship loadRequest:request];
_theWorship.frame = CGRectMake(0, 0, self.navigationController.view.bounds.size.width, self.navigationController.view.bounds.size.height);
[self.view addSubview:_theWorship];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_theWorship attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.bottomLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_theWorship attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_theWorship attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_theWorship attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]];
}
Get rid of addConstraint and call isActive = true instead. See the documentation:
When developing for iOS 8.0 or later, set the constraint’s active property to true instead of calling the addConstraint(_:) method directly. The isActive property automatically adds and removes the constraint from the correct view.
Alternatively use NSLayoutAnchor; its not as long winded as NSLayoutConstraint. I only use NSLayoutConstraint in a loop or when I cannot express a constraint with NSLayoutAnchor (ie center multiply).
-(void)viewWillAppear:(BOOL)animated {
**[super viewDidLoad];**
Change to:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
Also make sure you add a method to check if your code has already been called, otherwise it will get called multiple times /everytime the view will appear. You should rather call it from viewDidLoad method instead , but you can choose whatever as long as you don't call it multiple times.
Also , after adding everything you can call:
[_theWorship layoutIfNeeded];
i'm having a problem with embedded views and auto layout.
I've created a view, which is a little complex. So now
I want to refactoring this view and create some view components. I got one of the views and take together in one uiview class, and put all its logic there. Lets call this view as XView. All right until now.
So I tried to embed XView in the main view, to see the view works, with its new component. I put this commands:
xViewInstance = ...
[self.container addSubview:xViewInstance];
It doesn't work. the xViewInstance is bigger than the parent view. I want to resize xViewInstance.
So I googled for answers to see what's going wrong. And I found some answers that could helped me. I found PureLayout.
So I tried with it.
- (void)updateViewConstraints {
if (!self.didSetupConstraints) {
[self.xViewInstance autoPinEdgesToSuperviewEdges];
self.didSetupConstraints = true;
}
[super updateViewConstraints];
}
It didn't work. xViewInstance continues bigger than its parent.
I found another answer here in stack, a code that create constraints in code, to adjusts subviews programmatically. Again it didn't work.
Now I have no ideia whats could be. I'm thinking that could some priority of the xViewInstance constraints.
Have someone ever passed for this situation? I would be very grateful if anyone can give some advice about this.
I believe this post will solve your problem:
Use autolayout to set dynamic UIView to match container view
I tested it like this and it worked:
- (void)viewDidLoad {
[super viewDidLoad];
// Init with a huge frame to see if it resizes.
xView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 800)];
xView.translatesAutoresizingMaskIntoConstraints = NO;
xView.backgroundColor = [UIColor redColor];
[containerView addSubview:xView];
[self addConstraints];
}
- (void)addConstraints
{
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:xView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:xView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:xView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:xView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0]];
}
Wg
I have a subview and added 1 button. I want to add this button in the center of that view. I use Autolayout so i need to set the constraints for this button programmatically.
I tried this code but the play button is not in the center.
[self.moviePlayer.view addSubview:self.playbtn];
self.playbtn.translatesAutoresizingMaskIntoConstraints = NO;
[self.moviePlayer.view addConstraint:[NSLayoutConstraint constraintWithItem:self.playbtn attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
toItem:self.moviePlayer.view attribute:NSLayoutAttributeCenterX multiplier:0.667 constant:0]];
Please help me to correct it. Thanks in advance.
You can use the NSLayoutAttributeCenterX and NSLayoutAttributeCenterY attributes to center your play button like this:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initViews];
[self initConstraints];
}
-(void)initViews
{
self.picture = [[UIImageView alloc] init];
self.picture.image = [UIImage imageNamed:#"bg"];
self.playButton = [[UIButton alloc] init];
[self.playButton setImage:[UIImage imageNamed:#"playButton"] forState:UIControlStateNormal];
[self.view addSubview:self.picture];
[self.view addSubview:self.playButton];
}
-(void)initConstraints
{
self.picture.translatesAutoresizingMaskIntoConstraints = NO;
self.playButton.translatesAutoresizingMaskIntoConstraints = NO;
id views = #{
#"picture": self.picture,
#"playButton": self.playButton
};
// picture constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[picture]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[picture]|" options:0 metrics:nil views:views]];
// play button constraints
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.playButton attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.playButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
}
You get something like this:
You can use this code to center any item (es. self.btn) in its superview:
[self.btn.superview addConstraint:[NSLayoutConstraint
constraintWithItem:self.btn.superview
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.btn
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self.btn.superview addConstraint:[NSLayoutConstraint
constraintWithItem:self.btn.superview
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.btn
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
I also wrote an article about aligning any UIView objects without using Storyboards on my blog.