I'm implementing simple collection view with images inside cells. The task that I want to achieve is when user taps on the cell - there should be flip animation and some details have to appear at the same cell.
I've tried a lot of things to achieve that, for example I've added two views on the ContentView of the cell. And when user pushes the button I called transitionToView method and everything worked fine, except that when the list contained more than 9-10 images, after scrolling the list some cells started to duplicate "flipped" view with no reason. I turned off dequeueReusableCellWithReuseIdentifier function and everything worked fine, but on older devices like iPhone4, application worked to slowly.
So the best solution i found is this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
UICollectionViewCell *cell1 = [cv dequeueReusableCellWithReuseIdentifier:kCellID3 forIndexPath:indexPath];enter code here
UIView *contents = [[UIView alloc]initWithFrame:cell1.bounds];
contents.layer.borderColor = [[UIColor colorWithRed:0.119 green:0.108 blue:0.222 alpha:1]CGColor];
contents.layer.borderWidth = 10.0f;
contents.backgroundColor = [UIColor yellowColor];
[cell1.contentView addSubview:contents];
UIView *backgroundView = [[UIView alloc]initWithFrame:cell1.bounds];
backgroundView.layer.borderColor = [[UIColor colorWithRed:0.529 green:0.808 blue:0.922 alpha:1]CGColor];
backgroundView.layer.borderWidth = 4.0f;
backgroundView.backgroundColor = [UIColor greenColor];
cell1.selectedBackgroundView = backgroundView;
[cell1 bringSubviewToFront:cell1.selectedBackgroundView];
return cell1;
}
But is it possible to add some animation for the event when cell becomes selected?
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell1 = [collectionView cellForItemAtIndexPath:indexPath];
UIView *toSwitch = cell1.contentView;
[UIView transitionFromView:toSwitch toView:cell1.selectedBackgroundView duration:0.33 options:UIViewAnimationOptionCurveLinear |UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
}
also this attempt ruins my cells - when one or more of the cells are flipped some other start to copy it..
So I need an animation (What I achieved), but I need to keep other UICollectionView cells unique and don't reuse this flipped view..
please help! :) really desperate about this!
thanks in advance
Some Solution:
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
if(cell.selected)
{
[cell setSelected:NO];
[collectionView deselectItemAtIndexPath:indexPath animated:NO];
UIView *toSwitch = cell.contentView;
[UIView transitionFromView:cell.selectedBackgroundView toView:toSwitch duration:0.001 options:UIViewAnimationOptionCurveLinear |UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
}
}
Pretty good for a temporary solution.. anyone has some better advice?
When you scroll, "old cells" are reused - that's what makes your table view perform well. Of course, if the cell in question has a transitioned view, it will show that one instead.
So, like with the data in the cell which you also set anew in each call to the cellForItemAtIndexPath function, you have to set the right view to be visible - remember the state of a cell's view like you do with data, then show the according view when the cell is presented.
Related
I have a photo gallery in my app. where i can select multiple photos and can delete them. All is going on very well.
I just need to implement apple's default selection behavior as we can see in camera roll.
Right now my selection is like this
I have implemented didSelectItemAtIndexPath method as follows -
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Delegate cell %# : SELECTED", [self formatIndexPath:indexPath]);
MyCustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.label.backgroundColor=[UIColor greenColor];
}
- (NSString *)formatIndexPath:(NSIndexPath *)indexPath
{
return [NSString stringWithFormat:#"%ld", (long)indexPath.row+1];
}
And In MyCustomCell.m, I set the frame of label as rectangle as shown in fig (green).
And method setSelected looks like following:
- (void)setSelected:(BOOL)selected
{
UIView *blurView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 70)];
blurView.backgroundColor=[UIColor whiteColor];
blurView.alpha=0.5;
blurView.opaque=YES;
if (selected) {
self.label.backgroundColor = [UIColor greenColor];
[self.imageThumb addSubview:blurView];
}
}
So, Is it possible to have apple's default selection behavior ??
Any help will be appreciated. Thanks in advance.
In setSelected: of your cell, if selected is YES then add a white UIView with an alpha of 0.5 (or tweak the value to an opacity that looks good to you); if NO, remove that overlay view. Then when you add your checkmark image, make sure to layer it on top of the overlay view with [self.contentView bringSubviewToFront:checkmarkImageView].
Just add an UIView with the desired gray color over the UIImageView.
Set alpha to f.e. 0.5 and hidden = YES;
_blurView.hidden = YES;
_blurView.alpha = 0.5;
Then set hidden = NO if selected.
_blurView.hidden = NO;
You can add the UIView with InterfaceBuilder.
To make it look even better you might use an UIImageView with a nice texture image.
I have a UITableView populated with custom UITableViewCells. Within those custom cells, I have a UITextField and a "See More" UIButton. The purpose of the UIButton is to dynamically expand that particular UITableCell when the user wishes to read more of the text. In the same way, when the user wishes to return to the original size, the user clicks the Button again, and the UITableViewCell will shrink to the original size.
Since the cell isn't being selected, I setup an IBAction within the Custom Cell like such:
//Within CustomCell.m
- (IBAction)showMoreText:(id)sender
{
//instance bool variable to flag whether the cell has been resized
self.hasBeenResized = YES;
//turn off mask to bounds, otherwise cell doesnt seem to resize
[[self.cellView layer] setMasksToBounds:NO];
// Calculate the new sizes and positions for the textView and the button
CGRect newTextViewFrame = self.textView.frame;
newTextViewFrame.size.height = self.textView.contentSize.height;
self.textView.frame = newTextViewFrame;
CGFloat bottomYPos = self.textView.frame.origin.y + self.textView.frame.size.height;
CGRect buttonFrame = self.showMoreButton.frame;
buttonFrame.origin.y = bottomYPos;
self.showMoreButton.frame = buttonFrame;
// Call begin and end updates
[(UITableView*) self.superview beginUpdates];
[(UITableView*) self.superview endUpdates];
// Set mask and put rounded corners on the cell
[[self.cellView layer] setMasksToBounds:YES];
[[self.cellView layer] setCornerRadius:10.0];
}
Following this, I have this in my ViewController class:
// Within ViewController.m
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"heightForRowAtIndexPath");
CustomCell *cell = (CustomCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
if([cell hasBeenResized] == NO)
{
return cell.frame.size.height + 20;
}
else
{
return cell.frame.size.height + cell.textView.frame.origin.y + cell.textView.frame.size.height + cell.showMoreButton.frame.size.height + 20;
}
}
What happens now is I can see the custom cell change the size of its textview, however, the table does not update the row height for that particular cell. Checking on the If-else statement there, it appears that hasBeenResized is always false, even though I set it to YES within the IBACtion of the CustomCell.
I have looked at other solutions here, but they all seem to involve didSelectRowAtIndexPath, which I cannot use in this instance (I have another behavior for the cell when it is selected).
Am I doing this completely wrong? Ideally, what I would like to do is to have the "Show More" button animate downwards as the textview is expanded and vice versa when it's collapsed.
Thank you!
Method beginUpdates won't call reloadData for you - you have to do it manually.
For your case it would be best to call:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
And place your showMoreText code in tableView:cellForRowAtIndexPath: method (for selected cell only)
To Update your tableView, you have to reload it.
[tableView reloadData];
I'm currently working on an iPhone app that's doing some strange things with a UIScrollView inside a UITableView. This is my first foray into iPhone dev, so I'm sure it's something silly I'm missing.
In each UITableViewCell I am putting in a basic UITableViewCell. In that UITableViewCell is a Label and a UIScrollView.
The label and scrollview is setup and working properly, but when it first displays it is offset about 30 pixels down on the y axis than it should be, or is positioned by the XIB/NIB. I am not moving it around manually. The label shows up in the right spot. at 0,0. The UIScrollView should be showing up at 0,22 but is showing up closer to 0,40.
When I swipe to scroll the containing UITableView, then all the UIScrollViews will show up in the right spot assuming that when the UITableView scrolled that UITableViewCell went offscreen.
Here is the code for the UITableView.cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"GalleryRowCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
[cell.layer setMasksToBounds:TRUE];
[cell.layer setCornerRadius:10.0];
Contagion *c = (Contagion *)[self.dataSet objectAtIndex:indexPath.row];
GalleryRowViewController *subView = [[GalleryRowViewController alloc] initWithContagion:c];
[cell.contentView addSubview:subView.view];
subView.contagionName.text = c.name;
subView.contagion = c;
return cell;
}
Here is the code for my GalleryRowViewController.viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
self.imageScroll.delegate = self;
[self.imageScroll setBackgroundColor:[UIColor blackColor]];
[self.imageScroll setCanCancelContentTouches:NO];
self.imageScroll.indicatorStyle = UIScrollViewIndicatorStyleWhite;
self.imageScroll.clipsToBounds = NO;
self.imageScroll.scrollEnabled = YES;
self.imageScroll.pagingEnabled = NO;
NSInteger x = 0;
CGFloat xPos = 0;
for (x=0;x<=10;x++) {
UIImage *image = [UIImage imageNamed:#"57-icon.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[imageView setBackgroundColor:[UIColor yellowColor]];
CGRect rect = imageView.frame;
rect.size.height = 70;
rect.size.width = image.size.width;
rect.origin.x = xPos;
rect.origin.y = 5;
imageView.frame = rect;
[self.imageScroll addSubview:imageView];
xPos += imageView.frame.size.width+5;
}
[self.imageScroll setContentSize:CGSizeMake(xPos, [self.imageScroll bounds].size.height)];
}
--- EDIT FOR IMAGES ---
After App Loads: http://img809.imageshack.us/img809/4576/screenshot20110927at427.png
After Scrolling the rows offscreen and back: http://img690.imageshack.us/img690/9461/screenshot20110927at428.png
Well, as my previous response was at too low a level, let me take another shot at it.
First, I just noticed the core problem that you're using a viewcontroller for each cell. To quote Apple, " "A single view controller typically manages the views associated with a single screen’s worth of content." That would also get rid of your XIB (just manually configuring your scrollview), which I bet will get rid of your problem.
To proceed, your main choice is whether to create a ContagionTableViewCell class or not as suggested by Scott.
If so, following the Elements example, create a subclass of UITableViewCell ContagionTableViewCell with properties of a scrollView, a labelview and a contagion. Like they use a custom setter for the element, use one for the contagion, so that whenever it is assigned, it also updates the cells label (and associated pictures).
Move your imageScroll code from GalleryRowViewController.viewDidLoad into the ContagionTableViewCell init code. Put the image code into a new routine, which will be called from the contagion setter.
If NOT, then move the GalleryRowView Controller code into your UITableView. I suggest you take a look at cellForRowAtIndexPath in Apple's tableViewSuite, the fourth example on subviews. In particular, it shows this pattern of separating the creation of a cell (when you need a brand new one) vs configuring the cell (when reusing it). As you have 10 imageViews inside your scrollView, you'll have to decide whether to delete all those (and/or the scrollview), or just reach inside and update their images when a cell is reused.
Can you post a screenshot. Its a bit hard to visualize what you are describing. I'm not sure how you are computing y origin to be 22.
As a side note I believe its cleaner to do this by creatint your own TableViewCell subclass and use that instead of the default UITableViewCell. There is an example called Elements which shows how to do this properly: http://developer.apple.com/library/ios/#samplecode/TheElements/Introduction/Intro.html
Well, I don't know it's the cause of your problem, but you've definitely got an issue. Note that every time you are asked for a cell, you're adding the galleryRow subview. When a cell goes off-screen, it's put on the reusableCell queue. Then you're asked for another cell; you get it from the queue, it still has the old galleryRow subview, and now you add another one; so that's not good. You should either reuse or delete the old one.
Finally, why are you using UITableViewCellStyleSubtitle, and then not using any of the default fields in that UITableView?
I'm loading a custom nib file to customize the cells of a UITableView. The custom nib has a UILabel that is referenced from the main view by tag. I would like to know if it is possible to change the shadow color of the UILabel when the cell is selected to a different color so it doesn't look like in the screenshot.
I prefer to make the shadow color change inside the TableCell code to not pollute the delegate. You can override this method to handle it:
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animate
{
UIColor * newShadow = highlighted ? [UIColor clearColor] : [UIColor whiteColor];
nameLabel.shadowColor = newShadow;
[super setHighlighted:highlighted animated:animate];
}
You could change the label's shadow color in -tableView:willSelectRowAtIndexPath: in the delegate. For instance:
-(NSIndexPath*)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.shadowColor = [UIColor greenColor];
return indexPath;
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.textLabel.shadowColor = [UIColor redColor];
}
I had the same issue and none of the above solutions quite worked for me - I didn't want to subclass UITableViewCell and also had some tricky selected/highlighted state changes done programmatically, which did not play well with the solutions above.
MySolution:
What I did in the end is to use a second UILabel underneath the primary UILabel to act as a shadow. For that 'shadow' UILabel you can set the 'Highlighted Color' to 'Clear Color'.
Obviously you have to update the shadow label each time you update the primary label. Not a big price to pay in many cases.
Hope that helps!
The simple answer, at least for the example shown above, is to not display the shadow in the first place. Since you can't see the white-on-white anyway, set the shadowColor to -clearColor.
If you actually need a shadow though, overriding the -setHighlighted method is the best solution. It keeps the code with the cell, which I think is better than trying to handle it from the table view.
I have a UITableView with reorderable rows and I'm using the standard UITableViewCell.text property to display text. When I tap Edit, move a row, tap Done, then tap the row, the built-in UILabel turns completely white (text and background) and opaque, and the blue shade to the cell doesn't show behind it. What gives? Is there something I should be doing that I'm not? I have a hacky fix, but I want the real McCoy.
Here is how to reproduce it:
Starting with the standard "Navigation-Based Application" template in the iPhone OS 2.2.1 SDK:
Open RootViewController.m
Uncomment viewDidLoad, and enable the Edit button:
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
Specify that the table has a few cells:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 4;
}
In tableView:cellForRowAtIndexPath:, add a line to set the text property of a cell, and therefore to use the built-in UILabel subview:
// Set up the cell...
cell.text = #"Test";
To enable reordering, uncomment tableView:moveRowAtIndexPath:toIndexPath:. The default implementation is blank, which is fine in this case since the template doesn't include a data model.
Configure the project for the Simulator, OS 2.2.1, Build and Go. When the app comes up, tap Edit, then slide any row to a new position, tap Done, and then tap each row one at a time. Usually a tap will select a row, turn it blue, and turn its text white. But a tap on the row that you just moved does that and leaves the UILabel's background color as white. The result is a confusing white open space with blue strips on the edges. Oddly enough, after the first bogus tap, another tap appears to correct the problem.
So far I have found a hack that fixes it, but I'm not happy with it. It works by ensuring that the built-in UILabel is non-opaque and that it has no background color, immediately upon selection.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// hacky bugfix: when a row is reordered and then selected, the UILabel displays all crappy
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
for (UIView *view in cell.contentView.subviews) {
if ([[view class] isSubclassOfClass:[UILabel class]]) {
((UILabel *) view).backgroundColor = nil;
view.opaque = NO;
}
}
// regular stuff: only flash the selection, don't leave it blue forever
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
This appears to work, but I don't expect it to be a good idea forever. What is the Right Way to fix this?
This looks like a bug in UITableView's rendering, and you should file a Radar bug report on it. It's like the cells don't get refreshed properly after the move.
One way to work around this for now is to not use the built-in label, but roll your own in the cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
CGRect frame = cell.contentView.bounds;
frame.origin.x = frame.origin.x + 10.0f;
UILabel *textLabel = [[UILabel alloc] initWithFrame:frame];
[textLabel setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
textLabel.tag = 1;
textLabel.textAlignment = UITextAlignmentLeft;
textLabel.backgroundColor = [UIColor clearColor];
textLabel.textColor = [UIColor blackColor];
textLabel.font = [UIFont boldSystemFontOfSize:20.0];
textLabel.numberOfLines = 1;
textLabel.highlightedTextColor = [UIColor whiteColor];
[cell.contentView addSubview:textLabel];
[textLabel release];
}
UILabel *textLabel = (UILabel *)[cell viewWithTag:1];
textLabel.text = #"Test";
return cell;
}
I tried this, and it doesn't exhibit the same sort of white blank rectangle you see with the built-in label. However, adding another non-opaque view to the table cell might not be the best for overall rendering performance.
I don't know how major of a glitch this is, because Apple doesn't want you to persist a selection highlight on a table row (they've been enforcing this lately during the review process). You're supposed to place a checkmark or move on to the next level in the navigation hierarchy with a selection, at which point this white box would only be on the screen for a fraction of a second.
The trick in the solution from Brad appears to be:
textLabel.backgroundColor = [UIColor clearColor];
If you leave the background as the default you still get the problem even when you roll your own cells UITableViewCells.
The reason I left it as the default is because the documentation says it is less computationally costly to use opaque backgrounds. Ideally I wouldn't want to use [UIColor clearColor] to fix this bug.
Maybe a completely custom painted cell would somehow fix it. I haven't tried those before though.
Does anyone else have a solution for this?
Thanks for the info, I was searching how to erase the background color from a UILabel.
I used the following line:
textLabel.backgroundColor = [UIColor clearColor];
and worked perfectly!!!
thanks
Alejandra :)
Selections aren't meant to be shown for extended periods! (We got knocked on this for several of our apps)
??? That means Apple would not approve their own Calendar app on iPhone! When you go to edit the start and end times of the event, the start time is selected indefinitely, it only changes once the user taps to the next field.