Need some advice on how to approach a scenario using a Collectionview. In short, the app has a CV displaying images where you can tap a cell with a thumbnail of an image and it will then display a fullscreen view of that image. I'm accomplishing this by instantiating a new UIView (not from storyboard) inside didSelectItemAtIndexPath. So the fullscreen view of the image from the cell is just a new UIView triggered from tapping the cell and I set the view's image to be the same as the cell's image...simple enough. The fullscreen view also has a button that relates to each image. Tapping the fullscreen image closes the image and goes back to the CV. All of this works perfectly.
However, I just realized that I would also like to be able to swipe through all the images while in fullscreen mode...basically very similar to how the iOS photo album works. I was able to write some code pretty quickly to do this by adding a swipe gesture to didSelectItemAtIndexPath and set the action selector to a method to handle the swipes, which worked. However, the result of this was really just changing the image for the original cell selected (tapped). So I'm not able to keep track of the selected cell while swiping through the images in fullscreen mode.
So I need advice on how to approach this. I know there has to be examples out there for something like this, but I was unable to find any. Does anyone have any advice on how I should implement this? Thanks!
Code from didSelectItemAtIndexPath...
self.fullScreenImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width-10, self.view.bounds.size.height-15)];
self.fullScreenImage.contentMode = UIViewContentModeScaleAspectFit;
UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleRightSwipe:)];
[rightSwipe setDirection:UISwipeGestureRecognizerDirectionRight];
[self.fullScreenImage addGestureRecognizer:rightSwipe];
UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleLeftSwipe:)];
[leftSwipe setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.fullScreenImage addGestureRecognizer:leftSwipe];
if (!self.isFullScreen) {
self.fullScreenImage.transform = CGAffineTransformMakeScale(0.1, 0.1);
__weak BaseViewController *weakSelf = self;
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
NSLog(#"Starting animiation!");
weakSelf.view.backgroundColor = [UIColor blackColor];
weakSelf.myCollectionView.backgroundColor = [UIColor blackColor];
weakSelf.fullScreenImage.center = self.view.center;
weakSelf.fullScreenImage.backgroundColor = [UIColor blackColor];
weakSelf.fullScreenImage.image = [UIImage imageWithContentsOfFile:coffeeImageData.imageURL.path];
weakSelf.fullScreenImage.transform = CGAffineTransformIdentity; // zoom in effect
[weakSelf.view addSubview:self.fullScreenImage];
[weakSelf.fullScreenImage addSubview:likeButton]; // add the button to the view
}completion:^(BOOL finished){
if (finished) {
NSLog(#"Animation finished!");
weakSelf.isFullScreen = YES;
}
}];
return;
}
Handling the swipe gesture from...
- (void)handleLeftSwipe:(UISwipeGestureRecognizer *)sender {
// make sure indexForSwiping is not > than size of array
if (self.indexForSwiping != [self.imageLoadManager.coffeeImageDataArray count]-1) {
self.indexForSwiping += 1;
NSString *cacheKey = self.allCacheKeys[self.indexForSwiping];
if (cacheKey) {
[self.imageCache queryDiskCacheForKey:cacheKey done:^(UIImage *image, SDImageCacheType cacheType) {
if (image) {
[UIView animateWithDuration:1.0 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.fullScreenImage.image = image;
} completion:^(BOOL finished) {
NSLog(#"swiping");
}];
}
}];
}
}
}
This framework already have such functionality, so you can look into source code to understand how it works...
https://github.com/mwaterfall/MWPhotoBrowser
I would have the view be part of the viewController and not only part of your function. Then, have the viewController manage the swipe, image, and location of the image. When your collectionView is tapped, set the location in your viewController so that when a swipe is caught you can increment by one and update your view with the new image. Let me know if that wasn't clear enough and I can try clarifying
Related
I have a UIScrollView that has a bunch of items in it, similar to a facebook feed. In that feed I have a bunch of images that are being loaded from the internet. When the images are loaded they fade in. The problem is that if I am scrolling the scroll view, then the image animation to fade in doesn't occur until my finger is lifted off the scrollview. Is there any way to update the subviews of the UIScrollView view while scrolling?
-(void)loadStuff:(DipticView*)diptic{
//DipticView is a UIView subclass that holds the imageview and some other information
ConnectionManager *conman = [[ConnectionManager alloc] init];
WebImage *webimage = [diptic.item.images firstObject]; //webimage holds the imageurl and the image
[conman getImageWithURL:webimage.image_URL inBackgroundWithBlock:^(NSString *error, id image) {
webimage.image = image;
diptic.imageView.image = image;
diptic.imageView.alpha = 0;
diptic.userInteractionEnabled = NO;
[UIView animateWithDuration:.4 animations:^{
diptic.imageView.alpha = 1;
} completion:^(BOOL finished) {
diptic.userInteractionEnabled = YES;
}];
//The diptics are all subviews of the scrollview.
}];
}
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:.4 animations:^{
diptic.imageView.alpha = 1;
} completion:^(BOOL finished) {
diptic.userInteractionEnabled = YES;
}];
});
But I'm not sure that this method can work well with animateWithDuration.
I'm trying to show a fullscreen picture from a smaller one included in a custom UITableViewCell. My code is highly linked to this article
By the way, in this example, the frame : [[UIScreen mainScreen] bounds]is not the good one for me. It's the an UIScrollView's bounds that I've got. I this to add the main screen through a variable inside the cell directly when each cell is created. So I've customized the previous example like this :
//viewDidLoad
self.globalView.frame = [[UIScreen mainScreen] bounds];
//cellForRowAtIndexPath
[cell setFullScreenView:self.globalView];
//fullScreenMethod
if (!isFullScreen) {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
//save previous frame
prevFrame = imageView.frame;
[imageView setFrame:self.fullScreenView.frame];
}completion:^(BOOL finished){
isFullScreen = YES;
}];
return;
}
My problem is that the imageView's new frame is not a full screen but still the UIScrollView's one.
Thank you for your help !
Best approach is create one temporary UIImageView and Show it in full screen,
For animation simply add the temporary UIImageView to location where the image view exists and animate it to full-screen and do revers for normal
Add tap gesture to UIImageView and add this bannerTapped as selector
//This will create a temporary imaget view and animate it to fullscreen
- (void)bannerTapped:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"%#", [gestureRecognizer view]);
//create new image
temptumb=(UIImageView *)gestureRecognizer.view;
//fullview is gloabal, So we can acess any time to remove it
fullview=[[UIImageView alloc]init];
[fullview setContentMode:UIViewContentModeScaleAspectFit];
[fullview setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"bckgrd.png"]]];
fullview.image = [(UIImageView *)gestureRecognizer.view image];
CGRect point=[self.view convertRect:gestureRecognizer.view.bounds fromView:gestureRecognizer.view];
[fullview setFrame:point];
[self.view addSubview:fullview];
[UIView animateWithDuration:0.5
animations:^{
[fullview setFrame:CGRectMake(0,
0,
self.view.bounds.size.width,
self.view.bounds.size.height)];
}];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(fullimagetapped:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[fullview addGestureRecognizer:singleTap];
[fullview setUserInteractionEnabled:YES];
}
//This will remove the full screen and back to original location.
- (void)fullimagetapped:(UIGestureRecognizer *)gestureRecognizer {
CGRect point=[self.view convertRect:temptumb.bounds fromView:temptumb];
gestureRecognizer.view.backgroundColor=[UIColor clearColor];
[UIView animateWithDuration:0.5
animations:^{
[(UIImageView *)gestureRecognizer.view setFrame:point];
}];
[self performSelector:#selector(animationDone:) withObject:[gestureRecognizer view] afterDelay:0.4];
}
//Remove view after animation of remove
-(void)animationDone:(UIView *)view
{
//view.backgroundColor=[UIColor clearColor];
[fullview removeFromSuperview];
fullview=nil;
}
It can't take a full screen frame because its parent view is the scrollView. Either show view modally or somehow move imageView directly under view controller's main view.
I am trying to Display a an Image when the user Taps on the UIImageView. But before the user Taps the UIImageView the Image should not be shown, and after a few seconds the Image should disappear again. Does anyone know how to do this? I read through couple of Threads but they do not work with the latest Xcode as it appears. Thanks for your help and time.
Udate
Well, my code now looks like this:
-(void)imageTapped:(UITapGestureRecognizer*)recognizer
{
recognizer.view.alpha=0.0;
((UIImageView*)recognizer.view).image = [UIImage imageNamed:#"twingo_main.png"];
[UIView animateWithDuration:1.0 delay:2.0 options:0 animations:^{
recognizer.view.alpha=1.0;
} completion:^(BOOL finished) {
recognizer.view.alpha=0.0;
((UIImageView*)recognizer.view).image = nil;
recognizer.view.alpha=1.0;
}];
}
- (void)viewDidLoad
{
UIImageView *hiddenImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 30, 20, 20)];
hiddenImage.userInteractionEnabled=YES;
[hiddenImage addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)]];
[self.view addSubview:hiddenImage];
Well, now my question is, how do I need to set up the UIImageView in the View Controller?
In your UIViewController's viewDidLoad method:
...
UIImageView *hiddenImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 30, 20, 20)];
hiddenImage.userInteractionEnabled=YES;
[hiddenImage addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)]];
[self.view addSubview:hiddenImage];
...
UITapGestureRecognizer Handler:
-(void)imageTapped:(UITapGestureRecognizer*)recognizer
{
recognizer.view.alpha=0.0;
((UIImageView*)recognizer.view).image = [UIImage imageNamed:#"imageName"];
[UIView animateWithDuration:1.0 delay:2.0 options:0 animations:^{
recognizer.view.alpha=1.0;
} completion:^(BOOL finished) {
recognizer.view.alpha=0.0;
((UIImageView*)recognizer.view).image = nil;
recognizer.view.alpha=1.0;
}];
}
Set the image in the UIImageView to nil initially. Add a tap gesture recognizer to the UIImageView that, when fired, sets the image and starts a timer. When the timer completes, set your image back to nil.
Here is an another way to handle above with NSTimer..
-(void)imageTapped:(UITapGestureRecognizer*)recognizer
{
CustomPopUpView *lCustomPopUpView = [[CustomPopUpView alloc]init];
//Added your imageview to Custom UIView class
[self.window addSubview:lCustomPopUpView];
[self.window bringSubviewToFront:lCustomPopUpView];
mPopupTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(closePopUp) userInfo:Nil repeats:FALSE];
}
- (void)closePopUp{
if (mPopupTimer != nil) {
[mPopupTimer invalidate];
mPopupTimer = nil;
}
for (UIView *lView in self.window.subviews) {
if ([lView isKindOfClass:[CustomPopUpView class]]) {
[lView removeFromSuperview];
}
}
}
Its better to have view behind the UIImageView to handle the TapGesture.
But here is the fix for you code:
Add this to viewDidLoad method to setup you image view to handle tap gesture
//By default the UserInteraction is disabled in UIImageView
[self.testImageView setUserInteractionEnabled:YES];
UITapGestureRecognizer *tapgesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(ShowImage:)];
tapgesture.numberOfTapsRequired=1;
tapgesture.numberOfTouchesRequired=1;
[self.testImageView addGestureRecognizer:tapgesture];
here is the method handle the gesture event
-(void)ShowImage:(UIGestureRecognizer*)recognizer{
recognizer.view.alpha=0.0;
((UIImageView*)recognizer.view).image = [UIImage imageNamed:#"clone.jpg"];
[UIView animateWithDuration:1.0 delay:0 options:0 animations:^{
recognizer.view.alpha=1.0;
} completion:^(BOOL finished) {
//[self performSelector:#selector(hideImage:) withObject:recognizer.view afterDelay:10];
[UIView animateWithDuration:1.0 delay:3.0 options:0 animations:^{
recognizer.view.alpha=0.0;
} completion:^(BOOL finished) {
((UIImageView*)recognizer.view).image=nil;
((UIImageView*)recognizer.view).alpha=1.0;
}];
}];
}
If you set the alpha of view to 0, then your view will not receive any touch event further. So its best practice to set it again to 1.0 after removed the image from UIImageView.
You may want to consider using a UIButton. Detecting touches with these is easy - as is changing their image.
You could also subclass UIControl (see http://www.raywenderlich.com/36288/how-to-make-a-custom-control).
I want to start the UIPanGestureRecognizer right after adding it to a screenshot. Because the screenshot is created through code, when an item has been highlighted, the user won't press on the screen again. So... How do I start the recognizer programmatically?
UIView *snapshot = [cell snapshotViewAfterScreenUpdates:NO];
//use the cell to map the snapshot frame to the window because this does a perfect job of accounting for table offset, etc. Other methods put the view a little to the side or way off
CGRect newFrame = snapshot.frame;
newFrame.origin = [cell convertPoint:newFrame.origin toView:self.view.window];
[snapshot setFrame:newFrame];
[HelperMethods shadowForView:cell color:[UIColor blackColor] offset:CGSizeMake(1, 1) opacity:.7 radius:snapshot.frame.size.width/4];
//[self.view addSubview:snapshot];
newFrame.origin.y -=10;
//move the frame a little to let user know it can be moved
[UIView animateWithDuration:.2 animations:^{
[snapshot setFrame:newFrame];
}];
//add a long press that kills the tap if recognized
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(userCellDragged:)];
[pan setMinimumNumberOfTouches:1];
[pan setMaximumNumberOfTouches:1];
[cell addGestureRecognizer:pan];
You can always call the method on its own.
For example, you've added the selector
- (void) userCellDragged:(UIPanGestureRecognizer)sender;
For your pan gesture recognizer.
You could call this from anywhere in the view by simply adding
[self userCellDragged:nil];
Remember to add a parameter to this method something like:
if (sender == nil) {
// Triggered programmatically
}
else {
// proceed as normal
}
There is UICollectionView with cell. By tapping on the cell subview should be added.
Everything works, but... if I quickly tap second or third time, it add two or three subviews...
So how to properly do that?
This is the code of didSelectItemAtIndexPath:
recipeDetailView = [[RecipeDetailViewController alloc] initWithNibName:#"RecipeDetailViewController" bundle:nil];
[recipeDetailView.view setBackgroundColor: [UIColor colorWithPatternImage:[UIImage imageNamed:#"bgRecipe.jpg"]]];
[recipeDetailView.view setFrame: myFrame];
[UIView animateWithDuration:0.3
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
[recipeDetailView.view setFrame: CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:recipeDetailView.view];
[self addChildViewController:recipeDetailView];
[recipeDetailView didMoveToParentViewController:self];
}
completion:^(BOOL finished){
[recipeDetailView slideViewAdd];
}];
In didSelectItemAtIndexPath just add all your code into this.
if(recipeDetailView == nil)
{
//copy all your codes from above question.
}
And when your are removing the view just set the recipeDetailView to nil.
recipeDetailView = nil;
Either disable user interaction for a short time after the tap is detected.
Or, use a gesture recognizer to detect the tap, and another for multiple taps, and require the multiple tap recognizer to fail before the single tap recognizer fires.