I'm adding an MPMoviePlayerController to a container view like so:
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:self.videoURL];
. . .
self.moviePlayer.view.frame = self.frame;
[self addSubview:self.moviePlayer.view];
The self is the container view. This container view is instantiated off-screen when it is created by a view controller. So the container view resides in a view, and the MPMoviePlayerController resides in the container view.
When I animate the container view on screen, it works. Other views slide off and this view slides in. However, the MPMoviePlayerController stays put with its frame off-screen. So the MPMoviePlayerController's container view moves on screen, but it does not move with its container view. Is this not allowed, or am I missing something? The code for animating the container view is below:
[UIView animateWithDuration:ANIMATION_LENGTH animations:^() {
for(int i = beginTag; i < beginTag + [self subviews].count; i++) {
UIView *viewPointer = [self viewWithTag:i];
CGRect viewFrame = viewPointer.frame;
viewFrame.origin.x -= SCREEN_WIDTH_VC;
viewPointer.frame = viewFrame;
}
} completion:^(BOOL finished) {
[self.vidLoad playVideo];
}];
This takes care of animating elements that are on-screen off of the screen, and then bringing off-screen elements onto the screen. I'm glad to hear all suggestions!
Solved - this was a result of my own stupidity. The view hierarchy was indeed working, but when I assigned self.frame to moviePlayer.view.frame, it took the absolute coordinates of the container view, not relative coordinates.
A silly mistake. Basically, I just had to write
self.moviePlayer.view.frame = CGRectMake(0, 0, SCREEN_WIDTH_VC, SCREEN_HEIGHT_VC);
This would place it right on top of the container view, as these coordinates are relative to that container view and not the screen (I just confused myself).
Related
I have a window and a subview is added, this subview is the container of another view(accessed and added using NSViewController).
I have disabled autolayout and doing resizing from springs. The subviews gets resize correctly on window resize.
If I add / remove subviews keeping the window size same it works fine. But if I add subview and maximize it and then remove and then add, it gets jumbled.
Some time it happens straight forward as :
Open the main window (it opens in small size). Maximize it, then add the subview, the subview is added to its original size as drawn in xib. Expected behavior is the subview should get expand and cover the main window.
I am not able to find the solution. Please help me to fix this. The sample code and sample project is attached here.
//In AppDelegate
- (IBAction)buttonClicked:(id)sender {
if (!self.myVC) {
self.myVC = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
}
[self.containerView addSubview:self.myVC.view];
}
- (IBAction)clearClicked:(id)sender {
for (NSView *view in self.containerView.subviews) {
[view removeFromSuperview];
}
}
I got the answer,
Before adding the view to container view, I get the container's rect and set the frame for child view.
- (IBAction)buttonClicked:(id)sender {
if (!self.myVC) {
self.myVC = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
}
NSRect rect = NSMakeRect(0, 0, self.containerView.frame.size.width, self.containerView.frame.size.height);
[self.myVC.view setFrame:rect];
[self.containerView addSubview:self.myVC.view];
}
I am creating my own slide menu which is pretty simple.
I am creating a subview, adding it to the view and then sliding the self.view to the right by 170px which reveals the new subview (named secondView).
However, none of the buttons on the new view are clickable after moving the self.view to the right (as they were before I did this). So I image that because the view has moved, the view is the clickable region of the app.
Is there a way to move the content across as opposed to the view to allow the new view to be clickable.
The problem is:
Touches outside of a view's frame never trigger events to its subviews.
I mean, touches outside of self.view.frame never trigger event to secondView that is one of subviews of self.view.
see: Event Handling Guide for iOS
You can [self.view.window addSubview:secondView] and move both self.view and secondView.
[self.view.window addSubView:secondView];
CGRect viewFrame = self.view.frame;
CGRect secondFrame = secondView.frame;
viewFrame.origin.x += 170;
secondFrame.origin.x += 170;
[UIView animateWithDuration:0.2 animations:^{
self.view.frame = viewFrame;
secondView.frame = secondViewFrame;
}];
Note:
Above code may cause troubles if self is not window's rootViewController. e.g. in UINavigationController.
I am using a custom controller for transitions (could not use navigation controller due to inherent cycles in project, which would allow the navigation controllers stack to grow unbounded [which I assume would cause memory issues]). I am emulating a navigation controllers sliding animation (when transitioning to new screens) using UIView animateWithDuration:animations:completion:
When a button triggering the transition is pressed, the frame of the new view I want to transition to is set to an offscreen position. In the animation for the transition (UIView animateWithDuration:animations:completion:), the view currently on screen has its frame set to an offscreen position, and the new view is set to an onscreen position.
This is inside my custom controller for transitions:
CGFloat windowWidth = self.mainView.frame.size.width;
CGFloat windowHeight = self.mainView.frame.size.height;
CGRect offScreenLeft = CGRectMake(-1*windowWidth, 0.0, windowWidth, windowHeight);
CGRect onScreen = self.mainView.frame;
CGRect offScreenRight = CGRectMake(windowWidth, 0.0, windowWidth, windowHeight);
if (direction == TransitionDirectionForward)
{
if (dragBackgroundOnscreen){
[self.mainView addSubview:self.backgroundView];
[self.mainView sendSubviewToBack:self.backgroundView];
self.backgroundView.frame = offScreenRight;
}
self.currentViewController.view.frame = offScreenRight;
[UIView animateWithDuration:0.65
animations:^{
oldViewController.view.frame = offScreenLeft;
if (dragBackgroundOffscreen)
self.backgroundView.frame = offScreenLeft;
else if (dragBackgroundOnscreen)
self.backgroundView.frame = onScreen;
self.currentViewController.view.frame = onScreen;
}
completion:^(BOOL finished){
[oldViewController.view removeFromSuperview];
if (dragBackgroundOffscreen)
[self.backgroundView removeFromSuperview];
[oldViewController willMoveToParentViewController:nil];
[oldViewController removeFromParentViewController];
[self.currentViewController didMoveToParentViewController:self];
}];
}
else if (direction == TransitionDirectionBackward)
{
if (dragBackgroundOnscreen){
[self.mainView addSubview:self.backgroundView];
[self.mainView sendSubviewToBack:self.backgroundView];
self.backgroundView.frame = offScreenLeft;
}
self.currentViewController.view.frame = offScreenLeft;
[UIView animateWithDuration:0.65
animations:^{
oldViewController.view.frame = offScreenRight;
if (dragBackgroundOffscreen)
self.backgroundView.frame = offScreenRight;
else if (dragBackgroundOnscreen)
self.backgroundView.frame = onScreen;
self.currentViewController.view.frame = onScreen;
}
completion:^(BOOL finished){
[oldViewController.view removeFromSuperview];
if (dragBackgroundOffscreen)
[self.backgroundView removeFromSuperview];
[oldViewController willMoveToParentViewController:nil];
[oldViewController removeFromParentViewController];
[self.currentViewController didMoveToParentViewController:self];
}];
}
I also want the background (self.backgroundView) to remain static unless moving to a screen that has its own background (i.e. I dont want the background to slide if the new views background is the same background).
I am using TransitionDirectionBackward and TransitionDirectionForward just to differentiate between sliding left and sliding right.
Everything works great, except when transitioning involves a GMGridView. the problem when the Gridviews frame is set to an offscreen frame its really setting its currently selected page's frame to that offscreen frame. Other pages of the gridview are not bounded by this frame, so they can appear on screen even before the transition. I tried setting the frame and bounds property on the GridView's viewcontroller's view, but I can still get a page of the gridview appearing onscreen before the transition animation.
Anyone see a solution to this? I was trying to find a way to clip the view of the GridView during the transition so pages dont appear except for the currently selected page, but havent found anything useful.
UPDATE: I found a possible fix by setting alpha = 0.0 for cells that are visible but shouldnt be (later setting alpha = 1.0 when the transition animation is complete). However, I need to know which cells to do this for. I need a way to access the page that the GMGridView is currently on so I can set the adjacent page's cells to have an alpha of 0.0.
UPDATE: Found a way to get it to work by using myGridView convertPoint:(A cgpoint i found by trial and error to be on the first cell of a page.) fromView:myGridView.window. NOTE: I needed an if/else if to check if i was in lanscape left or landscape right since the window coordinates do not rotate when the device is rotated. with this i was able to get the index of the cell at the point on the screen i had specified and then set the previous page to be transparent until after the transition animation.
I would still like to know if there is a way of "clipping" the gridview so that if the cells could be opaque, but just never displayed....?
I think I over complicated the problem. I was looking for the clipsToBounds method of a UIView (although I could have sworn I tried that before). In any case, its working now!
I'm trying to create horizontal scroll view(s). Each view containing one image. [done]
Initially when app is opened I'd display couple of images i.e 3, so user can scroll back and forth between images. [done]
However I want to be able to go to another view controller and pick another image(s) maybe two for example and display five images in the scroll view instead of displaying 3 initially.
How would one do that? to "re-refresh" the initial scroll view ?
Update
Should I use delegate for this communication between view controllers? or how is this done?
1 main controller, other containing image selection?
This part above and much more explained by article here.(not advertising, I hope it will help someone as well).
Bounty update part 1 :
I think now that I found my way around delegates, I have additional question that I cannot find answer to, all examples I saw were about updating table views.
Bounty part 2 :
If I were to have a scrollview inside my view controller and some nsimages inside scroll view. i.e :
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *images = ...some images array;
for (int i = 0; i < images.count; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *subview = [[UIImageView alloc] initWithFrame:frame];
subview.image = [images objectAtIndex:i];
[self.scrollView addSubview:subview];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * images.count, self.scrollView.frame.size.height);
}
And let say my view controller implemented some delegate method didAddImage or didRemoveImage.
Meaning that about images array would get updated.
Actual bounty question :
How would one actually "tell" the view controller, O.K now your scrollview has one more image to display, please re-fresh or reload?
If I were to have a table view instead scroll view and was inserting images I'd do it something like this(in my delegate method) :
-(void) mockDelegatetablemethod:(...) ...{
[self.images addObject:image];
NSIndexPath *indexPath =
[NSIndexPath indexPathForRow:[self.images count] - 1
inSection:0];
[self.tableView insertRowsAtIndexPaths:
[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self dismissViewControllerAnimated:YES completion:nil];
}
How would one do this to the scrollview?
Bounty update part 3:
This is "simple" case described above, naturally I'd have to support removing images also as well as if one wants to remove all images and add some new.
How would one actually "tell" the view controller, O.K now your
scrollview has one more image to display, please re-fresh or reload?
Well, you would just add the new image as a subview of the scroll view. So something like:
- (void)addImage:(UIImage*)image {
[images addObject:image];
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * images.count;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *subview = [[UIImageView alloc] initWithFrame:frame];
subview.image = image;
[self.scrollView addSubview:subview];
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * images.count, self.scrollView.frame.size.height);
}
Or you could have a method that goes and "resets" the scroll view by removing all subviews and then re-adds all the images in your images array. Then call this method in viewDidLoad instead of your code there so that you're not duplicating code.
Basically, there's no silver bullet here. You roll your own.
You're going to need make your image scroller a class that you can tell to load up certain images. You'll need a reference to the existing image scroller, pass that pointer into your other "pick more images" controller. Then the "pick more images" controller can tell the image scroller that there is a new list of images.
This is already implemented by Apple in their iOS sample project "PhotoScroller". It has view reuse as well. They even have a WWDC 2010 video on this. I think its advanced scrollview techniques OR designing apps with scrollviews.
Shouldn't be hard to port over to mac
With the following code:
UIImageView *largeImageView = [[UIImageView alloc] initWithImage:theImage];
[largeImageView setContentMode:UIViewContentModeScaleAspectFit];
largeImageView.frame = [[UIScreen mainScreen] applicationFrame];
[viewController.view addSubview:largeImageView];
viewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:viewController animated:YES];
I would expect the image to be at the top of the View, not the bottom. I double-checked the origin x and y, and they are 0,0.
Here is a screenshot: http://cl.ly/8F3J
Thanks to Tommy for providing some thinking out loud and debugging help, I figured out what I was doing wrong. I changed around the order of operations and added the imageview as a subview after I pushed the viewcontroller on the nav stack. This fixed the issue as my view controller had it's new view from the nav controller.
largeImageView is a subview of viewController.view, so its coordinates are relative to that of its superview. Probably you want something more like:
// the bounds of viewController.view contain its correct size, but
// have an origin of (0, 0)
largeImageView.frame = viewController.view.bounds;
[viewController.view addSubview:largeImageView];
What's probably happening at the minute is that you're getting a frame much larger than the view controller's view size, then the fact that UIViewContentModeScaleAspectFit will be adding some space at the top and bottom of the view as necessary (assuming your image is proportionally wider than the target view) is pushing the image off the bottom of the screen.