I'm developing an iOS app with latest SDK.
I have an UITableView with a lot of rows but I only show five rows. In other words, the user only can see five rows. He/she can scrolls the table, but only five rows are visible.
I want to make selectable the third row. The user only can select the third visible row.
I'm trying to simulate an UIPickerView first, because I'm using Core Data and all the code now works fine with UITableView delegate and, second, I don't know how to customize UIPickerView background and selection area.
How can I make the third visible row the only row selectable?
implement -[id<UITableViewDelegate> tableView:willSelectRowAtIndexPath:] like this:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *indexPaths = [tableView indexPathsForVisibleRows];
if ([indexPaths objectAtIndex:2] isEqual:indexPath) {
return indexPath;
} else {
return nil;
}
}
More info here
Complete example along with snap to rows (by implementing ScrollView Delegate):
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#end
ViewController.m:
#import "ViewController.h"
static int kRowHeight = 92;
static int kArrayCount = 20;
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *tableViewData;
#end
#implementation ViewController
-(void)populateArray {
for (int i=0; i<kArrayCount; i++) {
[self.tableViewData addObject:[NSString stringWithFormat:#"String # %d",i]];
}
[self.myTableView reloadData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [self.tableViewData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = [self.tableViewData objectAtIndex:indexPath.row];
// Configure the cell...
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
// Standard UIAlert Syntax
UIAlertView *myAlert = [[UIAlertView alloc]
initWithTitle:#"Selected"
message:[NSString stringWithFormat:#"Selected Row %d", indexPath.row]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[myAlert show];
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *indexPaths = [tableView indexPathsForVisibleRows];
if ([[indexPaths objectAtIndex:2] isEqual:indexPath]) {
return indexPath;
} else {
return nil;
}
}
#pragma mark - ScrollView Delegate Methods (Make rows snap to position top)
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset {
int cellHeight = kRowHeight;
*targetContentOffset = CGPointMake(targetContentOffset->x,
targetContentOffset->y - (((int)targetContentOffset->y) % cellHeight));
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableViewData = [[NSMutableArray alloc] initWithCapacity:kArrayCount];
[self populateArray];
// Do any additional setup after loading the view, typically from a nib.
}
#end
You can use the array [self.tableView indexPathsForVisibleRows] to find out what indexPaths are visible. In the willSelectRowAtIndexPath method return nil for all but the third element.
To avoid highlighting you can also use this to set
cell.selectionStyle = UITableViewCellSelectionStyleNone;
In the CFRAIP method.
Related
I need idea how to implement this kind of custom UITableViewCell by animating, just like the out-of-the-box feature of UITableViewCell when sliding left or right.
What I've done so far (and is working), is:
Add a button with that circle image.
Toggle visibility of that button.
Toggle highlighted and normal state of the button.
Toggle constraints of the whole views when showing or hiding the radio button in #2.
Reload Data.
I'm thinking that this question might make my implementation cleaner when instead of toggling visibility and constraints of my custom tableViewCell, just slide it or animate it.
EDIT: These radio buttons appear only in a certain mode, like editing mode. So normally, there are no radio buttons in my cells.
You can try this I created Simple Demo.
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDelegate,UITableViewDataSource>
{
NSMutableArray *dataArray;
}
#property (weak, nonatomic) IBOutlet UITableView *mTableView;
// You can toggle the Selection by Means you can show hide the checkboxes
- (IBAction)didTapBringCheckBoxBtn:(id)sender;
#end
View controller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
dataArray=[[NSMutableArray alloc]init];
[dataArray addObject:#"Apple"];
[dataArray addObject:#"Mango"];
[dataArray addObject:#"Papaya"];
[dataArray addObject:#"Guava"];
[dataArray addObject:#"Pineapple"];
[dataArray addObject:#"Strawberry"];
[dataArray addObject:#"Banana"];
[dataArray addObject:#"Grapes"];
[dataArray addObject:#"Pomegranate"];
[dataArray addObject:#"Green Tea"];
[dataArray addObject:#"Raisin"];
self.mTableView.delegate=(id)self;
self.mTableView.dataSource=(id)self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Check Box Button Action
- (IBAction)didTapBringCheckBoxBtn:(id)sender {
if(self.mTableView.editing==YES)
{
[self.mTableView setEditing:NO animated:YES];
}else{
[self.mTableView setEditing:YES animated:YES];
}
}
#pragma mark - UITableView DataSource Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [dataArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [dataArray objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - UITableView Delegate Methods
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 3;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"user selected %#",[dataArray objectAtIndex:indexPath.row]);
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"user de-selected %#",[dataArray objectAtIndex:indexPath.row]);
}
#end
OutPut Will look Like This :
At Start Not showing Checkboxes :
On click Show CheckBox :
You did the harder, you just need to hide your button, here is a similar implementation I did to have a delete button and delete it without a confirmation.
1) Made a subview in content view, and made it bigger than content view with a negative leading space (-42 is the value of the slide offset)
Then put your checkmark button in this hidden space like I did for mine:
Then when you will set editing mode to true, your checkbox will appear with slide animation ;)
Lets consider your data source looks like
NSMutableDictionary *sampleRecord = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"isSelected",#"true",#"title",#"title text", nil];
records = [[NSMutableArray alloc] init];
[records addObject:[sampleRecord copy]];
[records addObject:[sampleRecord copy]];
[records addObject:[sampleRecord copy]];
And your table delegate/data source methods should be
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [records count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//Initialize your cell
//Read the all views from your cell
//Now add the click event to your radio button (UIButton)
[myButton addTarget:self action:#selector(changeRadioState) forControlEvents: UIControlEventTouchUpInside];
return cell;
}
- (IBAction)changeRadioState:(id)sender
{
//Detect the row index fro table where is the button present
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:tableView];
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:buttonPosition];
//Now get the resource data for clicked row
NSMutableDictionary *record = [records objectAtIndex:indexPath.row];
//Change the selected state images
if ([[records valueForKey:#"isSelected"] isEqualToString:#"true"]) {
[sender setImage:[UIImage imageNamed:#"unSelectStateImage"] forState:UIControlStateNormal];
[record setValue:#"false" forKey:#"isSelected"];
}
else{
[sender setImage:[UIImage imageNamed:#"selectedStateImage"] forState:UIControlStateNormal];
[record setValue:#"true" forKey:#"isSelected"];
}
//Reload entire table
[tableView reloadData];
//Or else you can reload the single row by
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
MGSwipeTableCell is an easy to use UITableViewCell subclass that allows to display swipeable buttons with a variety of transitions.
https://github.com/MortimerGoro/MGSwipeTableCell
I need to recreate a UITableView section on a UIButton click. Here is an example for this.
AND in button click it should be created like this.
But i am not able to implement this .
Please Help.
I am just creating a simple UITableView with a textLabel.
Code For cellForRowAtIndexPath is
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
return cell;
}
Create a integer variable index and property for tableview in .h
#property UITableView *tableView;
Then in -(void)viewDidLoad set index=0;
Now, write a method which is get called when you tap on add button
-(void)newTableView
{
tableView = [UITableView alloc] initWithFrame:CGRectMake(x,y+height*index,width,height)];
//other code related to table
tableView.tag = ++index;
[self.view addSubview:tableView];
}
Use tag in delegate methods
Edit: As you requested to add new section in Table View instead of new table view
You have to first create table view of grouped style
tableView = [UITableView alloc] initWithFrame:(CGRect)frame style:UITableViewStyleGrouped];
Now, When you hit on that add button call a method
-(void)newSection
{
count++;
[tableView reloadData];
}
Then, you have to implement this delegate methods for grouped table view
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return #"title";
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
//same as your previous code
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//same as your previous code
}
//
// ViewController.m
// DynamicNewSections
//
// Created by J Pulikkottil on 14/07/15.
// Copyright (c) 2015 J Pulikkottil. All rights reserved.
//
#import "ViewController.h"
#interface ViewController ()
#property(nonatomic) NSMutableArray *arrayData;
#property (strong, nonatomic) IBOutlet UITableView *tableViewTest;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.arrayData = [NSMutableArray array];
//section1
[self addSectionArray];
}
- (IBAction)addSection:(UIButton *)sender {
[self addSectionArray];
[self.tableViewTest reloadData];
}
- (void) addSectionArray{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"Height",#"label", #"", #"value", nil];
NSArray *arraysection = [NSArray arrayWithObjects:dict, nil];
[self.arrayData addObject:arraysection];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableViewTest.frame.size.width, 45)];
view.backgroundColor = [UIColor yellowColor];
return view;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return [self.arrayData count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [[self.arrayData objectAtIndex:section] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellDetails" forIndexPath:indexPath];
NSArray *arraysection = [self.arrayData objectAtIndex:indexPath.section];
NSDictionary *dict = [arraysection objectAtIndex:indexPath.row];
cell.textLabel.text = [dict valueForKey:#"label"];
cell.detailTextLabel.text = [dict valueForKey:#"value"];
return cell;
}
#end
Try this:
Just have a property (e.g. numberOfSections)
Set it to 1 in viewDidLoad
In the numberOfSectionsInTableView: return self.numberOfSections;
In the IBAction of your UIButton add this code:
self.numberOfSections++;
[self.tableView beginUpdates];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:self.numberOfSections-1]
withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView endUpdates];
I can't find a simple, concise answer anywhere and I refuse to believe that XCode makes things as hard as other tutorials I've found out there...
Say I have the following array
NSArray* days = [NSArray arrayWithObjects:#"Sunday",#"Monday",#Tuesday",#"Wednesday",#"Thursday",#"Friday",#"Saturday",nil];
I have a UI Table View, table_Days, that I would like to simply show the items from my array. What is the proper way to go about populating my table?
Here's my full explanation, starting with a case extremely similar to yours:
http://www.apeth.com/iOSBook/ch21.html#_table_view_data
So suppose days is stored as an instance variable accessed through a property self.days. Then set self as the table view's datasource and use this code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (!self.days) // data not ready?
return 0;
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.days count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"Cell"
forIndexPath:indexPath];
cell.textLabel.text = (self.days)[indexPath.row];
return cell;
}
You should populate your table view using the data source methods. Returning the count of the array for the number of rows.
If you need to detect when a user taps on a cell you can use the delegate methods.
#interface ViewController<UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, copy) NSArray *days;
#property (nonatomic, strong) UITableView *tableDays;
#end
#implementation ViewController
-(void)viewDidLoad
{
[super viewDidLoad];
UITableView *tableDays; // Set this up
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
tableDays.delegate = self;
tableDays.dataSource = self;
[self.view addSubview:tableDays];
self.tableDays = tableDays;
self.days = #[#"Sunday", #"Monday", #"Tuesday", #"Wednesday", #"Thursday", #"Friday", #"Saturday"];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.days count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.days[indexPath.row];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *day = self.days[indexPath.row];
NSLog(#"Day tapped: %#", day);
}
#end
You should consider using a UITableViewController if you just want to show a table view.
Note that its better practice to use camel case for variables.
When I click on a cell in my tableview, the app crashes with:
// QuestionViewController.h
#interface QuestionViewController : UIViewController <UITableViewDelegate , UITableViewDataSource> {
}
#property (nonatomic, strong) AppDelegate *app;
#property (nonatomic, retain) PFObject *feed;
#end
// QuestionViewController.m
#synthesize app, feed;
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
return [[feed objectForKey:#"options"] count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *cellTxt = [[[feed objectForKey:#"options"] objectAtIndex:indexPath.row] objectForKey:#"option_text"];
[[cell textLabel] setText:cellTxt];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"clicked cell");
}
- (void)viewDidLoad {
[super viewDidLoad];
app = [[UIApplication sharedApplication]delegate];
feed = [app.feed objectAtIndex:0];
}
I have implemented didSelectRowAtIndexPath, but it doesn't get called before crashing.
Other threads on SO suggest that I have unconnected outlets but I have checked that this isn't the case.
I am creating multiple instances of the above UIViewController like this:
for (int a = 0; a < totalQuestions; a++) {
QuestionViewController *temp = (QuestionViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"aQuestion"];
temp.view.frame = CGRectMake(self.view.frame.size.width*a+scrollWidthBeforeAppend, 0, 320, 443);
[scroller addSubview:temp.view];
}
And adding them to a scroll view. They display correctly, the UITableView is populated, and everything seems to work fine other than when I try and click on a cell. Any suggestions?
Your temp UIViewControllers get deallocated by the time you press a cell.
You should keep a reference to them to prevent this, for example in an array.
I'm building a app which has a table view. But I want this to be a table view which expands the cell when you tap on it and close when you tap a second time.
But I was wondering if the following is possible. When the cell isn't selected you only see a picture, title and the beginning of the text. But as soon as the cell is selected, it will expand and show even more subviews, i.e. image views.
Is this possible? For instance, to hide a subview in the cell, and as soon a it is tapped it's visible and aligned the right way? And of course, how do i do that?
Thnx!!!
I did something similar quite a few time ago. You'll find the code at github.
Note that it is very rough, as it where my beginning iPhone days, i.e. properties are missing.
.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
NSIndexPath *selectedIndexPath;
NSDictionary *articles;
}
#end
.m
#import "FirstViewController.h"
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
selectedIndexPath = nil;
articles = [[NSDictionary dictionaryWithObject:[NSArray arrayWithObjects:#"one", #"two", #"three",
#"four", #"five", #"six",
#"seven", #"eight", #"nine",
#"ten", #"eleven", nil]
forKey:#"title"] retain];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[selectedIndexPath release];
[articles release];
[super dealloc];
}
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[articles allKeys] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[articles allKeys] objectAtIndex : section];
}
- (int)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
id key = [[articles allKeys] objectAtIndex:section];
return [[articles objectForKey : key] count];
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ((selectedIndexPath != nil) && (selectedIndexPath.row == indexPath.row))
return 80.0;
return 40.0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * MyIdentifier = #"MyIdentifier";
UITableViewCell * cell = [self.tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
id key = [[articles allKeys] objectAtIndex:indexPath.section];
cell.textLabel.text = [[articles objectForKey:key] objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedIndexPath == indexPath) {
selectedIndexPath = nil;
} else {
selectedIndexPath = indexPath;
}
[self.tableView deselectRowAtIndexPath : indexPath animated : NO];
[tableView beginUpdates];
[tableView endUpdates];
}
#end
Yes, I do this in the app I'm working on now.
You need to keep track of the state that a cell is in, either open or closed. If only 1 cell can be open at a time, you can do this by just keeping a reference to the current indexPath. If multiple cells can be open at the same time, you'll need an array of booleans, which tracks if each one is open or closed.
In heightForRowAtIndexPath, just return the correct height based on if the row is open or closed.
In cellForRowAtIndexPath, if the row is closed, hide all the content that shouldn't be visible when it's closed. The views can still be there, but they should be set to hidden = YES.
Finally, in didSelectRowAtIndexPath, set the given index path to open if it was closed, and closed if it was open, then reload the cell with [tableView reloadRowsAtIndexPaths:]. If you are only allowing 1 at a time to be open, then just set your current open index path to the one that was selected, and reload both the one that was selected as well as the one that had been previously open.