I have a problem and I hope one of you can suggest a better way of accomplishing what I am trying to do.
So I have a UITableView with a list of items. Each tableview row has an "Add" button. When the add button is pushed, the selected item is added to master list. (Think of a to-do app), That works fine, no problems there. What I want is some type of notification that the item was added to the list. Such as a checkmark that appears on the same row that was added for two seconds then disappears. Here is my code:
-(void)addToList:(id)sender event:(id)event{
//This is the add button method on each cell.
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.theTableView];
NSIndexPath *indexPath = [self.theTableView indexPathForRowAtPoint: currentTouchPosition];
//Check image is the image of the checkmark that I want to display when the user adds an item.
checkImage.alpha = 0.0f;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.7f];
checkImage.alpha = 1.0f;
checkImage.frame = CGRectMake(currentTouchPosition.x + 100, currentTouchPosition.y, 25, 25);
[UIView commitAnimations];
....[only relevant code shown]...
}
When the user taps the add button, the checkImage fades in and positions it self where the user touched the row + 100 pixels to the right. That works fine, but it does not position the checkmark in the center of the row, it could be in the center, near the top or near the bottom, depending on where the user tapped the add button. I need it in the center, or a different way to go about this. Any suggestions? Thanks!
Once you get the indexPath, get the cell using,
UITableViewCell * cell = [self.theTableView cellForRowAtIndexPath:indexPath];
CGSize cellSize = cell.contentView.frame.size;
and later,
CGPoint touchPositionInCell = [self.theTableView convertPoint:currentTouchLocation toView:cell.contentView];
CGPoint checkmarkCenter = CGPointMake(touchPositionInCell.x + 100, cellSize.height/2);
CGRect checkmarkFrame = CGRectZero;
checkmarkFrame.origin = checkmarkCenter;
checkmarkFrame = CGRectInset(checkmarkFrame, -12, -12);
checkImage.frame = checkmarkFrame;
[cell.contentView addSubview:checkImage];
This should position the checkmark in the vertical center.
And I hope you are aware of concept of reusable cells. Try to remember the indexPath of the cell the checkImage is in. If you see a request for a cell at that indexPath then add checkImage as its subview.
Related
I got an gridview. Each cell within that grid is clickable. If a cell is clicked, another viewcontroller must be presented as a modal viewcontroller. The presentedviewcontroller must slide in fro the right to the left. After that, the modalviewcontroller can be dismissed with a slide. How do i achieve this? I got some images to show it :
Both views are separate viewcontrollers.
[Solution]
The answer from Matthew pointed me in the right direction. What i needed was a UIPanGestureRecognizer. Because UISwipeGestureRecognizer only registers one single swipe and i needed the view to follow the users finger. I did the following to accomplish it :
If i cell is tapped inside my UICollectionView, the extra view needs to pop up. So i implemented the following code first :
/* The next piece of code represents the action called when a touch event occours on
one of the UICollectionviewCells.
*/
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSString* release_id = releases[indexPath.row][0];
// Next boolean makes sure that only one new view can be seen. In the past, a user can click multiple cells and it allocs multiple instances of ReleaseViewController.
if(releaseViewDismissed) {
// Alloc UIViewController and initWithReleaseID does a request to a server to initialize some data.
ReleaseViewController *releaseViewController = [[ReleaseViewController alloc] initWithReleaseID: release_id];
// Create a new UIView and assign the height and width of the grid
UIView *releaseViewHolder = [[UIView alloc] initWithFrame:CGRectMake(gridSize.width, 0, gridSize.width, gridSize.height)];
// Add the view of the releaseViewController as a subview of the newly created view.
[releaseViewHolder addSubview:releaseViewController.view];
// Then add the UIView with the view of the releaseViewController to the current UIViewController's view.
[self.view addSubview:releaseViewHolder];
// Place the x coordinate of the new view to the same as width of the screen. Then after that get the x to 0 with an animation.
[UIView animateWithDuration:0.3 animations:^{
releaseViewHolder.frame = CGRectMake(0, 0, releaseViewHolder.frame.size.width, releaseViewHolder.frame.size.height);
// This is important. alloc an UIPanGestureRecognizer and set the method that handles those events to handleSwipes.
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipes:)];
// Add the UIPanGestureRecognizer to the created view.
[releaseViewHolder addGestureRecognizer:_panGestureRecognizer];
releaseViewDismissed = NO;
}];
}
}
Then my handleSwipes is as follows:
-(void)handleSwipes:(UIPanGestureRecognizer *)sender {
CGPoint translatedPoint = [(UIPanGestureRecognizer*)sender translationInView:self.view];
CGPoint translation = [sender translationInView:sender.view];
CGRect newFrame = [sender view].frame;
[sender setTranslation:CGPointZero inView:sender.view];
if (sender.state == UIGestureRecognizerStateChanged)
{
newFrame.origin.x = newFrame.origin.x + translation.x;
// Makes sure it can't go beyond the left of the screen.
if(newFrame.origin.x > 0) {
[sender view].frame = newFrame;
}
}
if(sender.state == UIGestureRecognizerStateEnded){
CGRect newFrame = [sender view].frame;
CGFloat velocityX = (0.3*[(UIPanGestureRecognizer*)sender velocityInView:self.view].x);
// If the user swipes less then half of the screen, it has to bounce back.
if(newFrame.origin.x < ([sender view].bounds.size.width/2)) {
newFrame.origin.x = 0;
}
// If a user swipes fast, the velocity is added to the new x of the frame.
if(newFrame.origin.x + velocityX > ([sender view].bounds.size.width/2)) {
newFrame.origin.x = [sender view].bounds.size.width + velocityX;
releaseViewDismissed = YES;
}
// Do it all with a animation.
[UIView animateWithDuration:0.25
animations:^{
[sender view].frame = newFrame;
}
completion:^(BOOL finished){
if(releaseViewDismissed) {
// Finally remove the new view from the superView.
[[sender view] removeFromSuperview];
}
}];
}
}
If you want the presented view controller to slide in from the right to the left, it cannot be a modal view. #Juan suggested one way to achieve the right to left and swipe back, but it would result in the grid view being pushed out of the way by the new view. If you would like the new view to cover the grid view when it slides in, you will either need to accept the vertical slide of modal views or write your own code to slide the view in from the right -- the latter would not actually be all that difficult*.
As for the swipe to get back, the easiest way to do that from either a modally presented view or a view you animate in yourself is to use a UISwipeGestureRecognizer. You create the recognizer, tell it what direction of swipe to look for, and you tell it what method to call when the swipe occurs.
*The gist of this approach is to create a UIView, add it as a subview of the grid view, and give it the same frame as your grid view but an x-position equal to the width of the view, and then use the following code to make the view animate in from the right.
[UIView animateWithDuration:0.3 animations:^{
slidingView.frame = CGRectMake(0, 0, slidingView.frame.size.width, slidingView.frame.size.height);
}];
I believe what you need is the following:
Create another controller that is going to handle navigation between these two (ContentViewController for example). This controller should have a ScrollView with paging enabled.
Here is a simple tutorial if you donĀ“t already know how to do this: click here
Once the cell is clicked you have to:
Create the new ViewController to be shown.
Enable paging and add this ViewController to the ContentViewController
Force paging to this newly created ViewController
Additionally you have to add some logic so that when the user swipes to change back to the first page, paging is disabled until a new cell is clicked to repeat the process.
I have a small 320x144viewcontroller named SubViewController.h which has a UITableView in it with 3 cells with a single section. I have made the tableView unscrollable and also put some shadow effect behind the tableView by grace of CALayer.
In another viewcontroller named as MainViewController.m i have added SubViewController.h as a subview to this MainViewController. Using UIPanGestureRecognizer i have successfully able to drag the SubViewContoller anywhere i want.
I make this subView visible with a UIBarButtonItem. And after selecting a cell in the tableView of the subview i made it disappear from main view with some animation.
Everything works fine.
But when i drag the subview and then try to select one cell i have to tap the cell twice. In first tap nothing actually happens except the cell turns blue(like it happens normally when you select a cell in tableView) but does not go Hidden. If i tap again then it will go hidden.
Without dragging the subview i can select one cell with a single touch and also the view goes hidden.
I have written the code for hiding the subview in didSelectRowAtIndexPath: method of the subview. And I have checked this method is not called when i select first time after dragging the subview.In the second tap or touch it is called though. And again if the user moves the subview again same problem occurs.
Surely some property of the subview got changed after dragging which i cant able to figure out.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = [touch tapCount];
switch (tapCount) {
case 1:
[self performSelector:#selector(singleTapMethod) withObject:nil afterDelay:.4];
break;
case 2:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(singleTapMethod) object:nil];
[self performSelector:#selector(doubleTapMethod) withObject:nil afterDelay:.4];
break;
. . .
}
First when u want your subView to be shown ,that is on click of your UIBarButtonItem:
-(IBAction)buttonClick
{
//setup ur view dynamically as you like//
PSview=[[UIView alloc]initWithFrame:CGRectMake(5, 5, 310,450)];
PSview.backgroundColor=[UIColor blackColor];
PSview.alpha=0.8;
[PSview.layer setBorderColor: [[UIColor whiteColor] CGColor]];
[PSview.layer setBorderWidth: 3.0];
PSview.contentMode=UIViewContentModeScaleAspectFill;
PSview.clipsToBounds=YES;
[PSview.layer setBorderColor: [[UIColor whiteColor] CGColor]];
[PSview.layer setBorderWidth: 3.0];
[PSview addSubview:subView];
[self.view addSubview:PSview];
}
then later :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//since there are two tables in one view, you can differentiate them using if()
if(tableView==subView)
{
// ...ur code . ..
// write your code what needs to happen when you click a row of your subView.
[PSview removeFromSuperview];
}
if(tableView==mainView)
{
// write your code , what happens when user clicks row of the main table
}
}
I want to select a table view row when the person reaches the center of the table view. Basically whenever the table reaches a point y I want the cell at that point to be selected.
I imagine it is similar to uiPickerView.
In your UITableViewController you should implement -scrollViewDidScroll: like this:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (prevCell != nil)
self.prevCell.selected = NO;
CGPoint offset = self.tableView.contentOffset;
CGPoint point = offset;
point.y += self.tableView.center.y;
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:point];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.selected = YES;
self.prevCell = cell;
}
This is tested and works. prevCell is a property of type UITableViewCell.
You must get the current offset of the scroll view for the table view and add this to the position of the cell you want (in this example, I take the cell in the center of the table view).
Then, using -indexPathForRowAtPoint: we grab the index path for the cell at the point and using -cellForRowAtIndexPath: we get the cell for the index path. Set it to selected and store it for later use (to deselect it on the next scroll).
have you tried
[[yourTableView cellForRowAtIndexPath:[yourTableView indexPathForRowAtPoint:wantPoint]] setSelected:true]
?
I think it can be achieved by getting all visible indexpaths, then data on visible indexpaths. The best answer you may see your own cz you know your problem well. Go through this class reference:
https://developer.apple.com/library/ios/#documentation/uikit/reference/UITableView_Class/Reference/Reference.html
just as a background, this question is related to this one I posted earlier: Trying to expand/collapse UITableViewCell from a UIButton on the custom cell
To summarize, I have a UITableView with several custom table cells. Each custom UITableViewCell has a text area, followed by a "View More" button. Essentially, each cell will initially display 3 lines of text, but when the user taps the "View More" button, the cell should expand to the entire text area height. Tapping the button again will collapse the cell.
I think this is difficult to visualize, so I have taken a short video here: http://dl.dropbox.com/u/762437/cell-animation.mov
(it's about a 3.5mb file, so shouldnt take that long to load). In the video, I show a cell doing the expand/collapse animation. When doing the expanding, the "View More" button animates downward. When tapping it again, it just jumps back up to its original position with no animation. Edit: Sorry, I should be more clear. The UITableView is animating the cells correctly, what I am asking is how to make the "View More" button animate correctly.
I've got the expand/collapse working (whether I did it right or wrong is another matter!), but the animations arent working as I expect. The expanding animation works, but not the collapsing.
In each custom UITableViewCell class, I have a IBAction that is called when the user taps the "View More" button. I also keep track of the cells that are currently expanded in a NSMutableArray. When the user taps the "Close" button, that cell is removed from the array.
In my tableView's cellForRowAtIndexPath, I check the array to see which cells should be expanded and which should show at their default size.
I am doing so with this code:
// Check if the array contains the particular cell, if so, then it needs to be expanded
if([expandedRows containsObject:indexPath])
{
// Expand the cell's text area to fit the entire contents
[cell.textLabel sizeToFitFixedWidth:cell.textLabel.frame.size.width];
// Find the new Y-position of where the "Close" button.
// The new Y position should be at the bottom of the newly expanded text label
CGFloat bottomYPos = cell.textLabel.frame.origin.y + cell.textLabel.frame.size.height;
// Get a rect with the "Close" button's frame and new Y position
CGRect buttonRect = cell.showAllButton.frame;
buttonRect.origin.y = bottomYPos;
// Animation block to shift the "Close" button down to its new rect
[UIView animateWithDuration:0.3
delay:0.0
options: UIViewAnimationCurveLinear
animations:^{
cell.showAllButton.frame = buttonRect;
[cell.showAllButton setTitle:#"Close" forState:UIControlStateNormal];
}
completion:^(BOOL finished){
NSLog(#"Done 1!");
}];
}
else
{
// This cell is currently collapsed
// Get the button rect and subtract the height delta to put it back
// OriginalCellSizes is where I keep track of the original Y positions
CGRect buttonRect = cell.showAllButton.frame;
buttonRect.origin.y -= cell.frame.size.height - [[originalCellSizes objectAtIndex:indexPath.row] floatValue];
// Animation block for the Button
[UIView animateWithDuration:0.3
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
cell.showAllButton.frame = buttonRect;
[cell.showAllButton setTitle:#"View More" forState:UIControlStateNormal];
}
completion:^(BOOL finished){
NSLog(#"Done! 2");
}];
}
Upon investigating, I found that in the "else" branch of that if-else, the buttonRect is already at the same Y position as the original position. I suspect that this is why there is no animation happening, bu I'm not sure.
Any help would be really great!
simple way to do this......
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
insertIndexPaths is an array of NSIndexPaths to be inserted to your table.
deleteIndexPaths is a array of NSIndexPaths to be deleted from your table.
Example array format for index paths :
NSArray *insertIndexPaths = [[NSArray alloc] initWithObjects:
[NSIndexPath indexPathForRow:0 inSection:0],
[NSIndexPath indexPathForRow:1 inSection:0],
[NSIndexPath indexPathForRow:2 inSection:0],
nil];
got it from this question...this question...
I have a tableview and when i scroll to the bottom of it and release my finger, only half of the cell is visible. If i keep draggaing towards the bottom the whole cell show and then hides when i release my finger.
Any idea?
The frame of your tableView seems to be set to a wrong frame. Reset the frame like this:
CGRect tableViewFrame = [tableView frame];
tableViewFrame.size.height -= heightOfButtonBar;
tableViewFrame.origin.y += heightOfButtonBar;
[tableView setFrame: tableViewFrame];