UITableView change during delete - uitableview

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.

Related

IOS/Objective-C: Slide updated view in from right on swipe

I would like to emulate the swipe to left to show next picture seen in the photos app, among other places so that the new picture slides in from right to left using animation.
However, in my case, the view has more than one photo. It has a caption as well.
I am able to do this without animation by just updating the photo and text upon swipe. This just changes the photo and caption on screen, however, without the right to left animation technique.
Is there a way to show the old view moving to the left while the new updated view moves in from the right.
The following moves the view to the left but renders the screen black as nothing is put in its place.
//In handleSwipe called by gesture recognizer
CGRect topFrame = self.view.frame;
topFrame.origin.x = -320;
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.view.frame = topFrame; _myImage=newImage;
_mycaption=newcaption;} completion:^(BOOL finished){ }];
Thanks for any suggestions.
Edit:
The view in question is a detail view that you get to from a tableview through a segue. It currently already uses a scrollview for vertical scrolling as the content can exceed a single screen in the vertical dimension.
I have found a way to get the old photo and caption to move left and the new to come in from right using a completion block, however, it is less than satisfactory, because there is a gap between the two actions when the screen is black. This seems endemic to completion approach because new image does not start to move until old image has completed moving.
- (IBAction)swipedLeft:(id)sender
{
CGRect initialViewFrame = self.view.frame;
CGRect movedToLeftViewFrame = CGRectMake(initialViewFrame.origin.x - 375, initialViewFrame.origin.y, initialViewFrame.size.width, initialViewFrame.size.height);
CGRect movedToRightViewFrame = CGRectMake(initialViewFrame.origin.x + 375, initialViewFrame.origin.y, initialViewFrame.size.width, initialViewFrame.size.height);
[UIView animateWithDuration:0.1
animations:^{self.view.frame = movedToLeftViewFrame;
//above moves old image out of view.
}
completion:^(BOOL finished)
{
self.view.frame = movedToRightViewFrame;
[self loadNextItem];//changes pic and text.
[UIView animateWithDuration:0.1
animations:^{self.view.frame = initialViewFrame;
//moves new one in
}
completion:nil];
}];
}
I suggest using a UIPageViewController, and manage each image/set of images as a separate view controller.
A page view controller supports either a page curl style transition or a slide transition. You'd want the slide transition.
Three ways to build this using existing components:
UIPageViewController (see DuncanC's answer).
UICollectionView with pagingEnabled set to true. Use a full-screen-sized cell and a UICollectionViewFlowLayout with scrollDirection set to horizontal.
UICollectionView as above, but instead of setting pagingEnabled to true, implement scrollViewWillEndDragging:withVelocity:targetContentOffset: in your delegate. This allows you to have a gutter between each cell while still having each cell fill the screen. See this answer.
It's ScrollView+PageControl
I hope it can help you.
- (void)viewDidLoad {
    [super viewDidLoad];
    self.scrollView = [[UIScrollView alloc] init];
    self.scrollView.delegate =self;
    self.scrollView.translatesAutoresizingMaskIntoConstraints =NO;
    self.scrollView.pagingEnabled =YES;
 self.scrollView.contentSize =CGSizeMake(ViewWidth*VIewCount, ViewHeight);
    [self.view addSubview:self.scrollView];
 self.pageControl = [[UIPageControl alloc] init];
   self. pageControl.translatesAutoresizingMaskIntoConstraints =NO;
    [self.view addSubview:self.pageControl];
    self. pageControl.numberOfPages = VIewCount;
    self.pageControl.currentPage = 0;
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView
                                                           attribute:NSLayoutAttributeCenterX
                                                           relatedBy:NSLayoutRelationEqual
                                                              toItem:self.view
                                                           attribute:NSLayoutAttributeCenterX
                                                          multiplier:1.0f
                                                            constant:0.0f]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[self.scrollView(width)]"
                                                                     options:0
                                                                     metrics:#{#"width":#(ViewWidth)}
                                                                       views:NSDictionaryOfVariableBindings(self.scrollView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[self.scrollView(height)]"
                                                                      options:0
                                                                      metrics:#{#"height":#(HEIGHT_VIEW)}
                                                                        views:NSDictionaryOfVariableBindings(self.scrollView)]];
     
   
  
 
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[_scrollView]-5-[pageControl(15)]"
                                                                      options:NSLayoutFormatAlignAllCenterX
                                                                      metrics:nil
                                                                        views:NSDictionaryOfVariableBindings(self.scrollView, self.pageControl)]];
//[imgArr addObject:[UIImage imageNamed:...]];
    for (NSInteger index = 0; index<ViewCount; index++) {
        UIImageView *addSubview = [[UIImageView alloc] initWithFrame:CGRectMake(index*ViewWidth, 0, ViewWidth, ViewHeight)];
// addSubView.image = [imgArr objectAtIndex:index];
        [self.scrollView addSubview:addSubview];
    }
}
 
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    NSInteger currentOffset = scrollView.contentOffset.x;
    NSInteger index = currentOffset / ViewWidth;
     
    if (currentOffset % ViewWidth == 0) {
        pageControl.currentPage = index;
    }
}
 

Animating a UIButton to fullscreen

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.

UICollectionView tapped animation like iPhone photos app

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.

iOS UITableView resize height / cant scroll down issue

Beginner iOS Developer here.
What I have is a UITableVIewController that has 1 section with 3 static grouped cells. Under that I have a UIView that has some buttons and text fields, when pressing one of the buttons, the UIView height increases. My problem is I cant scroll down to see the content that is at the bottom of the UIView
Screenshots:
When the green plus button is pressed, these elements are moved down making room for some new elements to be inserted (which I haven't implemented yet as I am stuck on this issue)
EDIT
Here is an example project: https://www.dropbox.com/s/vamxr12n6rrsam7/resizeSample.zip
You problem is that in your method to change the invoice. You only change the constraint of the invoiceBottomView, but you are not changing the size of invoiceBottomView's superview, that is the invoicePickerViewsContainer. So your tableView won't know you want to use more space of the tableFooterView (your invoicePickerViewsContainer).
You can change your code for this:
- (IBAction)invoiceLinesAddLine:(id)sender {
[UIView animateWithDuration:.25 animations:^{
CGRect invoiceBottomViewFrame = self.invoiceBottomView.frame;
NSInteger invoiceBottomViewFrameHeight = invoiceBottomViewFrame.size.height;
NSInteger invoiceLinesTopConstraintValue = invoiceLinesControlsViewTopConstraint.constant;
NSInteger invoiceLinesBottomConstraintValue = invoiceLinesControlsViewBottomConstraint.constant;
invoiceBottomViewFrameHeight = invoiceBottomViewFrameHeight + 40;
invoiceLinesTopConstraintValue = invoiceLinesTopConstraintValue + 40;
//invoiceLinesBottomConstraintValue = invoiceLinesBottomConstraintValue - 40;
invoiceBottomViewFrame.size.height = invoiceBottomViewFrameHeight;
invoiceLinesControlsViewTopConstraint.constant = invoiceLinesTopConstraintValue;
invoiceLinesControlsViewBottomConstraint.constant = invoiceLinesBottomConstraintValue;
CGRect invoicePickerViewsContainerFrame = self.invoicePickerViewsContainer.frame;
invoicePickerViewsContainerFrame.size.height += 40;
self.invoicePickerViewsContainer.frame = invoicePickerViewsContainerFrame;
self.tableView.tableFooterView = self.invoicePickerViewsContainer;
[self.view layoutIfNeeded];
} completion:^ (BOOL finished){
if (finished) {
//actions when animation is finished
}
}];
}
You don't need to change the invoiceLinesBottomConstraintValue, because you will increment the invoicePickerViewsContainer's height.
Then you need to add again the tableFooterView, so the table view will know that the size change, and the table will change the contentSize
Now, you have problems with the pickers... But I don't understand why you have the pickers in this view. If you want to preserve those pickers inside that view, you will need to change the constraint of those or its frame.
The way you setup the - (IBAction)invoiceLinesAddLine:(id)sender {} and the view within the tableview does not update the self.tableView.contentsize as you add more items. so add these lines to the completion function of the animation function within invoiceLinesAddLines and then twice the incremental height addition (i picked 80 arbitrarily)
completion:^ (BOOL finished){
if (finished) {
//actions when animation is finished
CGSize size=self.tableView.contentSize;
size.height+=80;
self.tableView.contentSize=size;
}
}];

UIView animation is not working as expected

I have two UIImageView filling the view controller, the first is filling the top half, and the second for the bottom half. I set them directly in the storyboard file.
In the viewDidLoad method, i am setting code in order to perform animation for the two UIImageView, so that it will look like a basket which gets opened (the first UIImageView moved to the top until it gets out of the view, and the second UIImageView moved to the bottom of the view until it gets out of the view).
This is my code so far:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGRect basketTopFrame = self.basketTop.frame;
basketTopFrame.origin.y = -basketTopFrame.size.height;
CGRect basketBottomFrame = self.basketBottom.frame;
basketBottomFrame.origin.y = self.view.bounds.size.height;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelay:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
self.basketTop.frame = basketTopFrame;
self.basketBottom.frame = basketBottomFrame;
[UIView commitAnimations];
}
But the behavior is different than i was expecting, the top frame is not moving, and the bottom frame is animated from top left to bottom (origin position, and not moving out of the view).
This is UIImageViews position as i set in the storyboard:
This is the UIImageViews when i launch the app:
And this is the UIImageViews when the app finish the animation(viewDidLoad):
Please note that this code works in Xcode 4.2, but since upgrading to Xcode 4.5 and using Storyboard, i begin getting this issue. Thanx in advance.
You may have auto layout enabled on your storyboard. In this case the view components will not have a valid frame value at viewDidLoad, so your code won't work.
You can disable auto layout by selecting the file inspector in the storyboard and unchecking "Use Autolayout".
In any case, that is the wrong method to be starting an animation from. Try moving the code to viewDidAppear.
Once you have "AutoLayout" turned off and if you are targeting IOS 4 and later than block based UIView animation is recommended by Apple as it simplifies the process and you can do things in one line. Please check UIView Reference for further information.
In your case , whether you plan to do the animation in ViewDidLoad / ViewWillAppear, it can be done in the following way using block based animation:
CGRect basketTopFrame = self.boxTop.frame;
CGRect basketBottomFrame = self.boxBottom.frame;
[UIView animateWithDuration: 0.5 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.boxTop setFrame:CGRectMake(basketTopFrame.origin.x, -basketTopFrame.size.height, basketTopFrame.size.width, basketTopFrame.size.height)];
[self.boxBottom setFrame:CGRectMake(basketBottomFrame.origin.x, self.view.bounds.size.height, basketBottomFrame.size.width, basketBottomFrame.size.height)];
} completion:^(BOOL finished) {
// TODO : Do any additional stuffs.
}];

Resources