I'm trying to have my collectionview cell/uiimageview go full screen when the image is tapped. I'm using animation and doing this from didSelectItemAtIndexPath and everything works and looks great, except that the imageview does not change size at all, and obviously is not going full screen like I want. I can see the animation working, however it all happens within the cell's preexisting size, and therefore, the image gets cropped. I would like it go full screen when tapped, similarly to the way it works in FB when tapping a photo. Appreciate any help! Here's my code...
#interface BaseViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UITextFieldDelegate> {
CGRect prevFrame;
}
#property (nonatomic) BOOL isFullScreen;
- (void)updateUI;
#end
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
ImageViewCell *selectedCell = (ImageViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
NSLog(#"didSelectItemAtIndexPath");
if (!self.isFullScreen) {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
NSLog(#"starting animiation!");
prevFrame = selectedCell.imageView.frame;
selectedCell.imageView.center = self.view.center;
selectedCell.imageView.backgroundColor = [UIColor blackColor];
//[selectedCell.imageView setFrame:[[UIScreen mainScreen] bounds]];
[selectedCell.imageView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
}completion:^(BOOL finished){
self.isFullScreen = YES;
}];
return;
} else {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
[selectedCell.imageView setFrame:prevFrame];
selectedCell.imageView.backgroundColor = [UIColor colorWithRed:0.62 green:0.651 blue:0.686 alpha:1];
}completion:^(BOOL finished){
self.isFullScreen = NO;
}];
return;
}
}
Your view cell is embedded in a view hierarchy, and most views clip their sub-views. To get a "full-screen" effect you're going to need to either (a) set parent views of your image view (the cell, the collectionView, anything that might be embedded in, etc) not not clip subviews (see: "clips to bounds") or (b) put your image in a view that is not embedded in those containers.
I don't recommend (a). Trying to set containers to allow child-views (your UIImageView) that are bigger than the container is kind of an anti-pattern that breaks the idea that containers manage and contain the display of their children.
For (b), what you would want to do is:
on Tap:
imageView = << create a new UIImageView >>
[self.view addSubview:imageView]
set imageView.image = collectionCell.imageView.image
set imageView.frame = << translated rect of the collectionCell.imageView.frame >>
... animate blowing your new imageView up to full-screen...
So you're basically treating the "tap on cell" action as a command to "create a new UIImageView to show the image, and animate that to full-screen."
Since the new image view is a direct child of the VC's root view, it can be sized full-screen without getting clipped by a parent view.
Related
so I have a very simple button that when clicked goes to fullscreen and when clicked again goes back to the same position it was initially in. For some reason it works perfectly without the animation. When I uncomment the animation part when I initially click the button it does nothing, the second time I click it slightly enlarges. The third time I click it animates slowly but back to it's smaller original size... Why is it animating the opposite way?
- (IBAction)viewImage1:(id)sender {
UIButton *btn = (UIButton*) sender;
if (btn.tag == 0)
{
CGRect r = [[UIScreen mainScreen] bounds];
/*[UIView animateWithDuration:0.5f delay:0.0f options:0 animations:^{*/
[sender setFrame: r];
/*}completion:nil];*/
btn.tag = 1;
}
else
{
btn.tag = 0;
[sender setFrame:CGRectMake(0,0,370,200)];
}
}
There are two solutions to your problem either of which will work:
Disable Autolayout. (discouraged)
You can do that in Interface Builder by opening the File Inspector
in the right pane and unchecking the respective check box.
However, if you want to use Autolayout for constraining other UI elements in your view (which is quite a good idea in most cases) this approach won't work which is why I would recommend the second solution:
Keep Autolayout enabled, create an outlet for your button in your view controller and set
self.myButton.translatesAutoresizingMaskIntoConstraints = YES;
in your view controller's viewDidLoad method.
You could also add layout constraints to your button and animate those. (This excellent Stackoverflow post explains how it's done.)
The reason for this tricky behavior is that once you enable Autolayout a view's frame is no longer relevant to the actual layout that appears on screen, only the view's layout constraints matter. Setting its translatesAutoresizingMaskIntoConstraints property to YES causes the system to automatically create layout constraints for your view that will "emulate" the frame you set, in a manner of speaking.
It is easier to do this with auto layout and constraints. Create an IBOutlet for the height constraint of your button call it something like btnHeight. Do the same for the width constraint call it something like btnWidth. Then create an IBAction like so:
- (IBAction)buttonPress:(UIButton *)sender {
UIButton *btn = (UIButton *) sender;
CGRect r = [[UIScreen mainScreen] bounds];
if (CGRectEqualToRect(btn.frame, CGRectMake(0.0, 0.0, 370, 200))) {
[UIView animateWithDuration:0.5 delay:0.0 options:0 animations:^{
self.btnHeight.constant = r.size.height;
self.btnWidth.constant = r.size.width;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){
}];
}else{
[UIView animateWithDuration:0.5 delay:0.0 options:0 animations:^{
self.btnHeight.constant = 200.0;
self.btnWidth.constant = 370.0;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){
}];
}
}
In my experience animating the frame of a UIButton does not work well, a, the only method, I'm aware of, is to use CGAffineTransformScale which will rasterize the title of the button and scale it as well.
I would like to replicate the exact same didSelect animation / segue when you tap a photo in the iPhone's photo app (or in almost every so other app) where the photo enlarges from the cell itself into a modal view controller, and minimizes to wherever it belongs to in the grid when dismissed.
I tried googling but couldn't find any articles about this.
There are many public repos on git that could probably do what you want. Some stuff I've found:
https://github.com/mariohahn/MHVideoPhotoGallery
https://github.com/mwaterfall/MWPhotoBrowser
Those may be overly complicated. Another option is creating a UIImageView at the same place as the cell and then animating it to fill the screen. This code assumes the collectionView has an origin at (0,0), if not then simply add the collectionView's offset when calculating the initial frame.
collectionView.scrollEnabled = false; // disable scrolling so view won't move
CGPoint innerOffset = collectionView.contentOffset; // offset of content view due to scrolling
UICollectionViewLayoutAttributes *attributes = [collectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0] ];
CGRect cellRect = attributes.frame; // frame of cell in contentView
UIImageView *v = [[UIImageView alloc] initWithFrame:CGRectMake(cellRect.origin.x - innerOffset.x, cellRect.origin.y - innerOffset.y, cellRect.size.width, cellRect.size.height)];
[self.view addSubview:v]; // or add to whatever view you want
v.image = image; // set your image
v.contentMode = UIViewContentModeScaleAspectFit; // don't get stupid scaling
// animate
[UIView animateWithDuration:0.5 animations:^{
[v setFrame:[[UIScreen mainScreen] bounds]]; // assume filling the whole screen
}];
It's not the nice popping animation but it should still look ok.
Does anyone know how to achieve the present view controller's view to shrink and put another one over it with a transparency? It is achieved in Tweetbot 3 when you tap on your avatar in the top left on the navigation bar. Should I take a snapshot for example?
In order to achieve this effect you will have to rebuild your view stack from scratch.
So as there is no possibility to change the viewController.view's frame, you'll have to add a kind of container subview a little like this:
#implementation ViewController
#synthesize container;
- (void)viewDidLoad {
[super viewDidLoad];
container = [[UIView alloc] initWithFrame:self.view.frame];
[self.view addSubview:container];
// add all views later to this insted of self.view
// continue viewDidLoad setup
}
Now if you have that, you can animate the shrinking behavior like so:
[UIView animateWithDuration:.5 animations:^{
container.frame = CGRectMake(10, 17, self.view.frame.size.width-20, self.view.frame.size.height-34);
}];
Okay, I assume you are developing for iOS 7, so we'll make use of some new APIs here (for earlier versions there are alternative frameworks). Now since WWDC UIView's got a resizableSnapshotViewFromRect:(CGRect) afterScreenUpdates:(BOOL) withCapInsets:(UIEdgeInsets) method returning a single UIView object.
[UIView animateWithDuration:.5 animations:^{
container.frame = CGRectMake(10, 17, self.view.frame.size.width-20, self.view.frame.size.height-34);
} completion:^(BOOL finished) {
UIView *viewToBlur = [self.view resizableSnapshotViewFromRect:container.frame afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
}];
If you do not want to rewrite your view management, you can also first take a snapshot of your main view this way, set it to the container and then animate only the image. But remember, you can not interact with the captured view then.
When you've got that, you can download the two category files from this repo (They're from WWDC, so directly from Apple!). Basically, what they do is, they add some cool new methods to the UIView class, of which we'll use applyDarkEffect. I haven't tested this, maybe another method fits your needs better here.
Anyways if we implement that into the block and maybe also add a UIImageView to display the blurred overlay, it should look something like this:
[UIView animateWithDuration:.5 animations:^{
container.frame = CGRectMake(10, 17, self.view.frame.size.width-20, self.view.frame.size.height-34);
} completion:^(BOOL finished) {
UIView *viewToBlur = [self.view resizableSnapshotViewFromRect:container.frame afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
UIImage *image = [viewToBlur applyDarkEffect];
UIImageView *blurredView = [[UIImageView alloc] initWithFrame:self.view.frame];
[self.view addSubview:blurredView];
// optionally also animate this, to achieve an even smoother effect
[blurredView setImage:image];
}];
You can then add your SecondViewController's view on top of the stack, so that it's delegate methods will still be called. The bounce effect of the incoming account view can be achieved via the new UIView animation method animateWithDuration:(NSTimeInterval) delay:(NSTimeInterval) usingSpringWithDamping:(CGFloat) initialSpringVelocity:(CGFloat) options:(UIViewAnimationOptions) animations:^(void)animations completion:^(BOOL finished)completion (more on that in the documentation)
I hope that will help you with your project.
I have a UITableView cell that has a custom label inside to handle the variable height. There is also an UIImage on the right and left.
When the table is toggled into edit mode, I want to inform and change each cell in the table, to format properly for being shifted to the right. And, when the user presses the small -, I want to further optimize to make room for the delete button.
Looking for a pattern that works for the above, assuming there is custom content in the cell that I have control over.
Thanks in advance!
Normaly all you need to do is set the springs and struts correctly and your content will slide correctly. If you create your sub-views in code then you need to make sure you call addSubview on the cell.contentView and not the cell.
To hide and / or resize the sub-views you need to override willTransitionToState:
- (void)willTransitionToState:(UITableViewCellStateMask)state
{
UIView *imageView = self.rightImageView;
UIView *labelView = self.centerTextLabel;
CGRect labelFrame = labelView.frame;
if (state & UITableViewCellStateShowingDeleteConfirmationMask) {
labelFrame.size.width += 52;
// Animating the fade while the image is sliding to the left
// is more jarring then just making it go away immediately
imageView.alpha = 0.0;
[UIView animateWithDuration:0.3 animations:^{
labelView.frame = labelFrame;
}];
} else if (!self.rightImageView.alpha) {
labelFrame.size.width -= 52;
[UIView animateWithDuration:0.3 animations:^{
imageView.alpha = 1.0;
labelView.frame = labelFrame;
}];
}
[super willTransitionToState:state];
}
I created a quick sample app up on GitHub that demos an iOS 4.3 app using a nib or code just uncomment //#define USE_NIB_TABLEVIEWCELL to use the nib
https://github.com/GayleDDS/TestTableCell.git
I personally prefer to create the table view cell in a nib file and only after profiling the App, replace the nib with code.
I am having a weird error while animating a couple of view in iOS. My goal is to switch from a custom "Split View". You can see what's going on in this youtube video: http://youtu.be/ZWbf2bQYMns
You can see the weird "bump" in the Y value of the UIImageView, and I have been wondering how to fix it for quite a while now.
This is the View Controller's interface:
#interface VideoSharing_Pad : UIViewController
{
IBOutlet UIView *videoCallView;
IBOutlet UIImageView *imageView; //This is "inside" mediaView
IBOutlet UIView *mediaView;
CGRect mediaRect;
CGRect videoCallRect;
CGRect imageRect;
}
In viewDidLoad I store both views doing:
//Get frames from XIB
mediaRect = mediaView.frame;
videoCallRect = videoCallView.frame;
imageRect = imageView.frame;
And this is the code that executes when I want to switch from the Split View to a full Screen Mode:
- (IBAction)toggleFullScreen:(id)sender
{
if (iScreenMode == callAndShareMedia) {
CGRect fullScreenRect = CGRectMake(0, 0, 1024, 768);
CGRect dissapearRect = CGRectMake(0, - videoCallView.bounds.size.height, videoCallView.bounds.size.width, videoCallView.bounds.size.height);
[UIView animateWithDuration:1.0
delay:0.1
options: UIViewAnimationCurveEaseOut
animations:^{
[videoCallView setFrame:dissapearRect];
[imageView setFrame:fullScreenRect];
[mediaView setFrame:fullScreenRect];
}
completion:^(BOOL finished){
}];
iScreenMode = onlyShareMedia;
return;
}
else if (iScreenMode == onlyShareMedia)
{
[UIView animateWithDuration:1.0
delay:0.1
options: UIViewAnimationCurveEaseOut
animations:^{
[videoCallView setFrame:videoCallRect];
[mediaView setFrame:mediaRect];
[imageView setFrame:imageRect];
}
completion:^(BOOL finished){
}];
iScreenMode = callAndShareMedia;
return;
}
}
I would really appreciate any help I can get. Thanks a lot!
this is a screenshot of the XIB:
as you can see from the screenshot and the .h file, the imageView is inside an UIView called mediaView, The other UIView, videoCallView is the one with the three dummy pictures.
Interesting question indeed. It definitely has to do with animating superview and subview at the same time. I did sample program, and reproduced similar situation.
My workaround would be to avoid animating the superview (mediaView), and expand only the subview (imageView) to full rectangle. Since your superview (mediaView) does not have much, it should not give so different user experience.
So, instead of
[UIView animateWithDuration:1.0
delay:0.1
options: UIViewAnimationOptionCurveEaseOut
animations:^{
[videoCallView setFrame:dissapearRect];
[imageView setFrame:fullScreenRect];
[mediaView setFrame:fullScreenRect];
}];
You can do
[UIView animateWithDuration:1.0
delay:0.1
options: UIViewAnimationOptionCurveEaseOut
animations:^{
[videoCallView setFrame:dissapearRect];
[imageView setFrame:(CGRect){fullScreenRect.origin.x - mediaRect.origin.x, fullScreenRect.origin.y - mediaRect.origin.y, fullScreenRect.size}];
}];
For coming back to normal mode, you can just ignore mediaView animation. Probably you want to move (animate) the toggleButton along with other animation as well.
#jrturton's answer (second part) seemed a nice workaround, but it did not work on my sample code. It worked on the way to go (expansion), but bumped on the way back (shrink), for the reason I don't know why. But don't dismiss his answer because of my comment, it could be me.
Interesting question. I can't view your video from work but I expect your issue is that you are resizing both a view and its subview during an animation, there will probably be interference from any autoresizing masks (do you have them?) - the superview will change the size of the subview, then the interpolated frame will be applied.
If you think about it there will also be a stage where your image view has to animate more quickly than the superview as it has more ground to cover to get to the same final rect. The interpolation worked out by the animation may struggle with this.
If removing any autoresizing masks doesn't work, you might need to split the animation into two - one to increase the size of the superview, and another to then zoom the image view to full size.