I have a a custom UITableViewCell class and in it I have a UIImageView. This UIImageView is only displayed when the text of the UITableViewCell is equal to a certain string, eg "Home". When the method mapView:regionWillChangeAnimated: is called, I want to apply an animation on this UIImageView.
My question is how can I, when mapView:regionWillChangeAnimated: is called, go through my UITableView and custom UITableViewCells and perform an animation on the UIImageView within the cells which have the UIImageView set as visible?
I've been able to call methods on cells before using the UITableView delegate methods but I'm struggling to find a solution in this scenario because the the UITableView is not being directly called by the map panning method, it's not a didSelectRowAtIndex path issue?
Thank you.
If you have reference to table view in your map delegate method, you can get all visible cells using visibleCells method in UITableView and apply animation for them if needed. General structure may look like:
// Controller
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
...
[self applyCellsAnimation];
}
- (void)applyCellsAnimation {
for (CustomCell* cell in [self.tableView visibleCells]) {
[cell performImageAnimationIfNeeded];
}
}
// CustomCell
- (void)performImageAnimationIfNeeded {
if (!self.myImageView.hidden) {
// perform animation
}
}
You can get an array of all the visible rows in your table view using
[myTableView visibleCells]
You then just need to integrate through each cell and animate the image.
i.e.
for (MyCustomCell* cell in [myTableView visibleCells])
{
[cell animateImage];
}
The implementation of animateImage can then do whatever you want - just check the imageView.hidden property and animate it if the imageView is visible.
Related
I have UITableViewCell that contains UIView (lets call it CPView) which is created while cellForRowAtIndexPath is called. CPView is just a plain coloured view and for every cell its width is different (that's why needed to create in cellForRowAtIndexPath).
Problem is
1)The CPView 's colour gets darker every time cell loads (May be due to every time that cell creates the same view so overlapping effect).
2) The cell overlaps / inherits other cell's CPView (we can see this because of light and dark colour of two CPView).
How can I prevent cell to recreate if it already exist or creation of this CPView again?
Edit
- (void)configureCell:(CreditDebitCell *)cell atIndexPath:(NSIndexPath *)indexPath {
//other code
UIView * CPView;
if (CPView){
CPView =nil;
}
else
{
CPView = [[UIView alloc] initWithFrame:CGRectMake(cell.bounds.origin.x, cell.bounds.origin.y, cell.frame.size.width*[self.percentArray[indexPath.row] floatValue] ,cell.frame.size.height )];
[CPView setClipsToBounds:YES];
[CPView setBackgroundColor:[UIColor colorWithRed:107/255.0 green:15/255.0 blue:47/255.0 alpha:0.5]];
[cell addSubview: CPView];
}
}
The issue here is reuse of the cells - and therefore you get multiple views added to your cell view.
You can:
-remove subview
-check if subview exists and do/don't do anything.
You can check if the subview is there by going through subviews:
for (UIView *v in cell.contentView.subview) {
if ([v isKindOfClass:[CPView class]]) {
// remove or flag that it exists
}
}
But I think that you should handle this in your cell - not your view controller that implements table view delegate. Better tell cell to use some view/hide some view based on some kind of logic then to do that inside cellForRowAtIndexPath
According to your i question(without cellforRowAtIndexpath) i can assume that you should check every time something like in cellForRowAtIndexPath
if(cpView){
cpView = nil;
}
// alloc again with required size for particular row.
Make a subclass of your UITableViewCell and make a property of it that will reference your CPView. This will now let you have a better control whether your subclassed cell does / doesn't have any CPView that needs to be added.
I have a Custom UITableView Cell as xib.
I have taken an Scroll View inside it.
I Know how to set the delegate but I have confusion.Their is two of doing this.
I don't knw which is best and How to decide which way I have to choose.
1 Way : To set Delegate To Files Owner
2 Way : setting Delegate to UITableViewCell
In a tableviewcell the delegate of scrollview will always be set to UITableViewCell
That means your second step will work .
Let me know if you find any difficulty.
The choice depends on what the delegate function for the inner scroll view must do. It is simpler to point the delegate to the custom cell and handle the inner scrolling events there.
But if handling that inner scrolling requires a lot of data and logic from the view controller, then point the delegate to the vc. To make that work, we'll need to know that the scroll event was from an inner scroll view, not the table, and we probably need the row where the scrolling happened, so:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// if the table view's delegate points here (likely), then to distinguish...
if (scrollView != self.tableView) {
// one of the scroll views in the table, but which one?
NSIndexPath = [self indexPathContainingView:scrollView];
// here we know that horizontal scrolling happened on indexPath.row
}
}
// return the indexPath of the tableView cell containing a view
- (NSIndexPath *)indexPathContainingView:(UIView *)view {
while(view && ![view isKindOfClass:[UITableViewCell self]])
view = view.superview;
return [self.tableView indexPathForCell:(UITableViewCell *)view];
}
I have a collection view cell with a number of subviews. One of the subviews I want to remove altogether so the adjacent subview can expand to fill that space via constraints.
This constraint configuration set up within the prototype cell in storyboard, and the subview is already in place in the prototype, ready to be removed after the cell has been instantiated.
My UICollectionViewCell subclass has a setter that does this:
- (void)setThing:(NSString *)aThing
{
if (!aThing)
{
[self.thingContainerView removeFromSuperview];
return;
}
// proceed if aThing exists
And when the cell is setup:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"mycellid" forIndexPath:indexPath];
cell.aThing = nil; //this should trigger removal of one of the cell's subviews
return cell;
}
The problem is when the UICollectionViewCell is first loaded, the subview has not been removed. Once I've scrolled the cell offscreen and returned to it, the subview has been removed as expected. So I'm assuming there's some sort of issue with the cell not being fully laid out when I'm setting the property the first time around.
How to get this to work?
I don't think the view has been laid out at that point so the subview cannot be removed. Try removing it in a different method such as;
- (void) didMoveToSuperview
{
if (!self.aThing)
{
[self.thingContainerView removeFromSuperview];
}
}
This will be overridden in your collection view cell.
There might be a more appropriate method to call this in but this should work.
I have a UITableView which has some custom cells. Within the custom cells I have a main UIImageView. When the cell is created I add a Tap Gesture Recogniser to the image view.
When the image is tapped I run the following:
- (void) handleImageTap:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"Image tapped");
UIImageView *imageView = (UIImageView *)gestureRecognizer.view;
// send the image instead of self when firing the segue
[self performSegueWithIdentifier:#"remindMeTurnInfo" sender:imageView];
}
I then pass the image into a new view controller in prepareForSegue method:
if ([segue.identifier isEqualToString:#"remindMeTurnInfo"]) {
UIImageView *imgView = (UIImageView *)sender;
MESPlayedTurnReminderViewController *VC = segue.destinationViewController;
VC.turnImage = imgView.image;
}
Question
1. I need a reference to the cell that the UIImageView is within, that was tapped. So the user taps one of the cells images, and I need to know which cell (indexPath) that image was tapped from. 2. I am finding that sometimes when the image is tapped the didSelectRowAtIndexPath is being called. This is incorrect and it should not be called, only the relevant handleTap method from the Gesture Rec. should be called. How can I ensure that the didSelectRowAtIndexPath is not called, as I need to run some other code when the cell is actually (correctly) selected.
There are two ways this is commonly done. Either add a tag to your image view in cellForRowAtIndexPath that's equal to indexPath.row, or search up through the hierarchy of views from the image view until you find one that's a UITableViewCell (or subclass). That can be done like this:
- (void) handleImageTap:(UIGestureRecognizer *)gestureRecognizer {
UIImageView *imageView = (UIImageView *)gestureRecognizer.view;
UIView *superview = imageView.superview;
while (![superview isKindOfClass:[UITableViewCell class]]) {
superview = superview.superview;
}
NSLog(#"indexPath.row is: %d", [self.tableView indexPathForCell:(UITableViewCell *)superview].row);
}
The reason to search rather than using something like imageView.superview.superview (which would work in iOS 6), is that the hierarchy can change in different versions of iOS, and in fact, it did change between iOS 6 and iOS 7.
As for your second question, it probably happens because you're accidentally tapping the cell rather than the image view. Other than making the image view larger so it's easier to tap, I don't see a fix for that.
1.- To get the cell you can check the superview of the UIImageView tapped
- (void) handleImageTap:(UIGestureRecognizer *)gestureRecognizer
{
UIImageView *imageView = (UIImageView *)gestureRecognizer.view;
UITableViewCell *cell = (UITableViewCell *)imageView.superview;
}
2.- To disable the cell selection on a UITableView
[self.yourTableView setAllowsSelection:NO];
I have a nib with a table cell, and within that table cell I have a UILabel. I want to make that label sizeToFit which means I have to do:
- (void)viewDidLoad {
[self sizeToFit];
}
However my label doesn't have any code, just an outlet to a variable in my controller, so there's nowhere I can put that code to effect the label.
I attempted making a sub class of UILabel (fitUILabel : UILabel) and then I clicked on the label in the nib and set its class to fitUILabel, however it does not seem to run.
In my controller right before the return statement in cellForRowAtIndexPath I tried putting
[cell.myLabelOutletVariable sizeToFit]
And this seems to work, however it only works on the recycled rows and not the labels contained in the initial cells of my table. This also seems to cause my text to flow right out of the cells and overlap onto others, however it does align it to the top which is what I wanted.
I assume you mean that your nib contains a UITableViewCell as a top-level object, and the table view cell has a UILabel subview.
The viewDidLoad method is defined on UIViewController, and UITableViewCell doesn't inherit from UIViewController.
UITableViewCell is a subclass of UIView. The proper place for a view to adjust the frames of its subviews is in its layoutSubviews method. You need to make a subclass of UITableViewCell, and set that as the custom class of the cell in your nib. Then, in your subclass, define this method:
- (void)layoutSubviews {
[self.label sizeToFit];
}
In your table view data source's tableView:cellForRowAtIndexPath: method, you may want to send setNeedsLayout to the cell after setting the text of the label. This will ensure that the cell receives layoutSubviews again, if it's being reused.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = ...;
cell.customLabel.text = [self labelTextForIndexPath:indexPath];
[cell setNeedsLayout];
return cell;
}
Alternatively, you could make a UILabel subclass, like this:
#implementation MyLabel
- (void)layoutSubviews {
[self sizeToFit];
}
- (void)setText:(NSString *)text {
[super setText:text];
[self setNeedsLayout];
}
#end
and then set MyLabel as the custom class of the label in your nib. But having a view set its own frame in layoutSubviews seems a little fishy to me, so I usually avoid it.
You should put your code for size the Label in Table view's Delegate Method CellForRowAtIndexPath and not in viewDidLoad.
If your Label is in Table View Cell.
You may rewrite the -(void) awakeFromNib method for initializing the Label.