I have implemented tableview in my viewcontroller. I have not given any cell selection style.
What problem I am facing is, when I click on any of the cell it becomes gray. Normally what should happen is only background should become gray and not the whole cell. But here whole cell becomes gray.
Can anyone provide solution for this?
Thanks in advance!
You will have to implement your custom selection style as following steps.
First, you will want to disable the default selection behaviour:
- (void)awakeFromNib {
[super awakeFromNib];
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
Second, override setHighlighted method (when user tap the cell and not yet raise his finger):
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
[super setHighlighted:highlighted animated:animated];
// Configure the view for the selected state
self.YOURVIEW.background = [UIColor grayColor];
}
Third, override setSelected method (when user tapped and raise his finger):
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
self.YOURVIEW.background = [UIColor grayColor];
}
Finally, you will want to deselect the cell in your tableview at a proper time by calling:
[self.tableView deselectRowAtIndexPath:animated:];
In your cell's setSelected method don't call the super's one, and implement your own solution. (Like graying out only the background view.)
Related
I have an NSCollectionView that is showing some images. I have implemented an NSCollectionViewDelegate to tell it which items should be selected and/or highlighted. I'm using a stock NSCollectionViewItem to draw the images and their names. When the user selects an item, my delegate gets the messages about highlight state changes:
- (void)collectionView:(NSCollectionView *)collectionView
didChangeItemsAtIndexPaths:(NSSet<NSIndexPath *> *)indexPaths
toHighlightState:(NSCollectionViewItemHighlightState)highlightState
{
[collectionView reloadItemsAtIndexPaths:indexPaths];
}
I do a similar thing for didSelect/didDeselect:
- (void)collectionView:(NSCollectionView *)collectionView
didSelectItemsAtIndexPaths:(nonnull NSSet<NSIndexPath *> *)indexPaths
{
[collectionView reloadItemsAtIndexPaths:indexPaths];
}
In the NSCollectionViewItems view, I do the following:
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
NSColor* bgColor = [[self window] backgroundColor];
NSColor* highlightColor = [NSColor selectedControlColor];
NSRect frame = [self bounds];
NSCollectionViewItemHighlightState hlState = [collectionViewItem highlightState];
BOOL selected = [collectionViewItem isSelected];
if ((hlState == NSCollectionViewItemHighlightForSelection) || (selected))
{
[highlightColor setFill];
}
else
{
[bgColor setFill];
}
[NSBezierPath fillRect:frame];
}
The problem I'm seeing is that drawing the highlight or selection appears to be random. When it does draw the selection, it's almost always on the items the user has actually selected (though it often leaves off the last item for some reason). Occasionally, it will select a different item the user did not click on or drag over. Often, though, it just doesn't draw.
I've added printing to verify that it is calling -didChangeItemsAtIndexPaths:toHighlightState: and -didSelectItemsAtIndexPaths:. Is there anything I'm doing wrong here?
I've added some logging to the view's -drawRect: method, and it doesn't appear to be getting called on all transitions, even though I'm calling -reloadItemsAtIndexPaths: in the -didChange* methods. Why not?
I've also noticed that the delegate's -should/didDeselectItemsAtIndexPaths: does not seem to get called ever, even though the -should/didSelectItemsAtIndexPaths: does get called. Why is that?
The problem turned out to be calling [collectionView reloadItemsAtIndexPaths:]. When you do that, it removes the existing NSCollectionViewItem and creates a new one (by calling your data source's collectionView:itemForRepresentedObjectAt:). That immediately sets the new collection view item to not selected (or rather it doesn't set it to be selected). When that happens, it won't call your should/didDeselect methods because the existing item doesn't exist anymore, and the new one is not selected.
The real solution turned out to be to subclass NSCollectionViewItem and override -setSelected: to do the following:
- (void)setSelected:(BOOL)selected
{
[super setSelected:selected];
[self.view setNeedsDisplay:YES];
}
When the view's -drawRect: method gets called, it asks the item if it's selected and draws appropriately.
Therefore, I could completely remove all of the should/did/select/Deselect methods from the delegate without any problem, and it all just worked!
Been trying to get the button to change from "edit" to "done". I am doing a tutorial and it seems to work on their end but not mine. I can't seem to find the issue.
The code:
- (IBAction)onEditButtonTapped:(UIBarButtonItem *)sender {
[self.tableView setEditing:true animated:true];
sender.style = UIBarButtonItemStyleDone;
sender.title = #"Done";
}
You are doing it the hard way. UIViewController already provides such a button.
Lets assume you want this button on the right side of the navigation bar.
In viewDidLoad, you want:
self.navigationItem.rightBarButtonItem = [self editButtonItem];
Then to handle the button, simply override the setEditing:animated: method:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self.tableView setEditing:editing animated:animated];
}
The button returned by the editButtonItem method is setup to call the setEditing:animated: method for you. There's no setup required.
I assume this means you have added your own table view to a UIViewController. If you are using a UITableViewController then you don't need the call to self.tableView setEditing... since it is already done for you.
If you really want to use your own button. Make sure you set the possibleTitles property of the UIBarButtonItem.
In my UIActionSheet I have a button which mark the touched array as favorite. When an array is favorite, I have a star that shows before the array's name. The favorite icon has an alpha 0, and by touching the "Add as Favorite" button the alpha changes to 1. The arrays are located inside an UIViewController, which shows 1 array in each cell.
The problem I have is that I have to reopen the ViewController for the star to show.
How can I fix so the star shows immediately when I press the "Add as Favorite" button, without reopening the ViewController?
The Favorite Icons alpha is updated under:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
And this is the code that updated the alpha:
if ([FavoriteArray containsObject: [mainArray objectAtIndex:indexPath.row]])
{
favoriteImage.alpha = 1;
}
Thanks!
You need to monitor when the action sheet has been actioned on, and update the collection view accordingly. An example implementation is as follows:
UIActionSheet* actionSheet = ...;
// Set yourself as the action sheet delegate, so you can monitor when events occur
actionSheet.delegate = self;
...
// This is called when a user selects an item within the action sheet
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
// kFavouriteButtonIndex is the index of the action sheet item to favourite
if (buttonIndex == kFavouriteButtonIndex) {
// Reload your collection view to update the cells
[self.collectionView reloadData];
}
}
Instead of calling reloadData, if you know the indexPath of the item which is being favourited, then you can call reloadItemsAtIndexPaths:#[indexPath] to make the process more efficient.
You will want to update the image's alpha when a button is selected. You can do this by implementing the UIActionSheetDelegate protocol in your UIViewController. You can do this in you View Controller's .m file:
#interface MyViewController : UIViewController <UIActionSheetDelegate>
Then override the actionSheet:clickedButtonAtIndex: method in the .m file:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
//make sure that the button pressed is the one which should change the image alpha:
if(buttonIndex == 2){ //put the button index in the place of "3"
//your code to update the alpha here
}
}
Make sure that you set the UIActionSheet's delegate to self:
myActionSheet.delegate = self;
I got a really strange problem.
My tableView has all the delegate and datasource set up.
Everything is fine.
However, clicking the rows do not activate:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
I used custom cells.
After I click and click and click and click and click, sometimes it goes through.
I wonder what can possibly cause that? It's as if the customCell is "absorbing" the touch event or something or what?
Could that be why? If so, if we want to implement customCell and we want the tableView to handle the touch up event, what should we do?
Additional symptom:
If I remove user interaction enabled from the custom cell then the problem is solved with a catch.
However, clicking the button will somehow erase all the label texts in the customCell.
The implementation of the custom Cell is the following:
- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz
{
if (self.biz == nil) //First time set up
{
self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right
NSString * className = NSStringFromClass([self class]);
//PO (className);
[[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
self.frame =self.view.frame;
[self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though
}
if (biz==nil)
{
return self;
}
_biz = biz;
self.prominentLabel.text = [NSString stringWithFormat:#"Isi: %#", biz.isiString];
self.Title.text = biz.Title; //Let's set this one thing first
self.Address.text=biz.ShortenedAddress;
//if([self.distance isNotEmpty]){
self.DistanceLabel.text=[NSString stringWithFormat:#"%dm",[biz.Distance intValue]];
self.PinNumber.text =biz.StringPinLineAndNumber;
NSString * URLString=nil;
if(biz.Images.allObjects.count!=0){
//self.boolImage=[NSNumber numberWithBool:true];
Image * image=(biz.Images.allObjects)[0];
URLString = image.URL;
URLString = [NSString stringWithFormat:#"http://54.251.34.144/thumbnailer/Thumbnailer.ashx?imageURL=%#",URLString.UTF8Encode];
//url=[NSURL URLWithString:image.URL];
}else{
float latitude = biz.getCllLocation.coordinate.latitude;
float longitude = biz.getCllLocation.coordinate.longitude;
URLString = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/staticmap?&zoom=16&size=160x160&maptype=roadmap&sensor=true¢er=%f,%f&markers=size:small|color:blue|%f,%f",latitude,longitude,latitude,longitude];
URLString = URLString.UTF8Encode;
}
//Should add code and add loading indicator here
[BGHPTools doBackground:^{
UIImage * imageBiz = [BGMDImageCacherAndDownloader getImageFromURL:URLString];
[BGHPTools doForeGround:^{
self.Image.image=imageBiz;
[self.Image makeRound];
}];
}];
//self.view=self.view;
/*if (self.tableViewCell == Nil)//Instantiate that tableviewCell
{
PO(self.tableViewCell);
}
self.tableViewCell.business = bis;
self.pinLbl.text = bis.StringPinLineAndNumber;
self.lblTitle.text=bis.Title;
//self.pinLbl.text=bis.pinNumber;*/
//}
/*self.name=[dict objectForKey:#"Title"];
self.address=[dict objectForKey:#"Street"];
CLLocation * cll=[[CLLocation alloc]initWithLatitude:[[dict objectForKey:#"Latitude"] doubleValue] longitude:[[dict objectForKey:#"Longitude"] doubleValue]];
self.distance=[NSNumber numberWithDouble:[cll distanceFromLocation:[cachedProperties currentLocation]]];*/
return self;
Update: I already figure out why the texts are gone. Turns out my background is white. When a row got selected, the text suddenly turn into white. So by setting selected style to blue I sort of get that "fixed".
However, I still do not see where in my code I specify that all label texts should be white if the underlying tableViewCell is selected.
After all, what's selected is the cell, not the label. How the hell the label knows that it has to turn white is beyond me.
If you are using a Storyboard to handle the interface, instead of using:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Try using
#pragma mark --- Push selectedObject to the detailView ---
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
RRAppDelegate *myAppDelegate = [[UIApplication sharedApplication]delegate];
if ([[segue identifier] isEqualToString:#"PushObjectSegue"]) {
NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow];
RRObjectViewController *detailViewController = [segue destinationViewController];
detailViewController.selectedObject = [myAppDelegate.goals objectAtIndex:selectedRowIndex.row];
}
}
I was having the same problem with the method you used and instead used this, it started working perfectly. Of course you'd have to adapt the code to your app's viewControllers and data source because I used my AppDelegate as the datasource, and I wasn't using a custom cell.
The most likely thing is that a view in your custom cell is absorbing the touch. Sometimes this is what you want, e.g. a button that does something, rather than selecting the entire cell. Assuming you don't want this, then just set those views' userInteractionEnabled property to NO.
--Additional code for custom NIB loading.
All you have to do is register the NIB in your viewDidLoad routine:
[tableView registerNib: [UINib nibWithNibName:#"yourCellNibName" bundle:nil] forCellReuseIdentifier:#"yourCellTypeID"]
and then in your cellForRowAtIndexPath just call:
newCell = [tableView dequeueReusableCellWithIdentifier #"yourCellTypeID"];
...
return newCell;
And it will load a cell from your XIB (or give you one from the previously used queue).
I just want to update that I think I have figured out what the problem is but still can't solve that quite right yet. And well the update is comprehensive so I think it should be an answer though I hope it's not the answer because some puzzle is still missing.
All the problem is interrelated.
The problem is in this line:
[self addSubview:self.view];
I basically turn that into:
Basically the my custom view cell has a view whose type is also tableViewCell. That view cover the real tableViewCell.
That's why when user interaction is enabled, that view will absorb the user's interaction.
That's also why the label "disappear". What happen is the label doesn't disappear. The label got highlighted and become white. However, what's highlighted is the tableViewCell not the opague view. The white opague self.view is still white while the tableCell itself is tinted with blue. So the label becomes white in the middle of white background and is gone.
I think I should replace [self addSubview:self.view] into self= self.view
However, that would mean changing the value of self. Yes it's in init. But it's still awkward. If anyone has the WAY to implement custom subclass of UI with XIB it'll be great because I haven't found one till now.
Awkward.
I wonder if we can draw a pointer to an XIB and specify that the outlet is self itself.
If that fail, I'll set background of self to white and background of self.view to transparent.
After tons of error and trying I did this:
self.contentView.backgroundColor = [UIColor whiteColor];
//self.view.backgroundColor = [UIColor redColor];
self.frame =self.view.frame;
/*PO(self.view.subviews);
PO(self.subviews);
PO(self.Title.superview);
PO(self.Title);
PO(self.view);
PO(self.Title.superview);
PO(self.view.contentView);*/
//Suck all the subviews from my minions
for (UIView* aSubView in self.view.contentView.subviews) {
[self.contentView addSubview: aSubView];
//[self.contentView add]
}
Basically I "move" all the subViews of my view object to my self object. There is a catch though that when subclassing tableViewCell I should move the subviews of the contentView. Who knows why.
At the end I just set self.view to nil for it's no longer needed and my program works as expected.
Also to set background of your tableViewCell, you need also to set the background of self.contentView rather than self.view.
Another approach you can try is to use story board. Alternatively you can just move the contentView of the self.view to self.
Make sure you'r implementing that method and not
deselectRowAtIndexPath:animated
I have a UITableView with some custom cells in it. In these custom cells I defined a UILongPressGestureRecognizer that triggers the edit mode of this table. So when someone presses and holds a cell for like 1.5 sec, the table goes into edit mode.
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(startEditMode:)];
Which triggers:
- (void)startEditMode:(UISwipeGestureRecognizer *)recognizer {
if (self.allowEdit) {
UITableView *table = (UITableView *)self.superview;
[table setEditing:YES animated:YES];
}
}
But what I want to do is detect when the table goes into edit mode because I need to show/hide some additional buttons in this case. But for some reason in my viewcontroller this is never executed:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
NSLog(#"SET EDITING");
[super setEditing:editing animated:animated];
}
Any suggestion why? Is this just being called when using a proper Edit Button as provided by default in the UINavigationController?
Or how can I detect when my UITableView goes into Edit Mode?
You're sending the message (setEditing) to the table view, you should be sending it to the view controller (presumably a UITableViewController subclass?). It will then take care of the table view for you.
Ok so in case someone else walks into this thread with the same problem, I will show you how I solved this.
In my custom UITableViewCell I have this method now:
- (void)startEditMode:(UISwipeGestureRecognizer *)recognizer {
if (self.allowEdit) {
UITableView *table = (UITableView *)self.superview;
UITableViewController *control = (UITableViewController *)table.dataSource;
[control setEditing:YES animated:YES];
}
}