I implemented a UITableview with all methods.
When the UITableviewController is the initial View Controller, it works fine, I can swipe to delete.
But when the UITableViewController is part of my project and reached by pushing the view, I can't swipe to delete. It seems the application doesn't always detect the swipe or something like this, because sometimes, the delete button appears.
This is a very simple project, there is no big operation.
(I'm using Google Analytics et Google Adsense library, but not in this view controller.)
---
EDIT : Some code and a question, the code of the previous screen may influence the performance of the uitableviewcontroller ?
My storyboard is like this :
[Navigation controller] --> [Root View Controller] --> [Table View Controller]
Code :
VerreTableViewController.h
#interface VerreTableViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *table_view_verres;
#end
VerreTableViewController.m
#interface VerreTableViewController ()
#end
NSArray *objects_verres;
#implementation VerreTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
objects_verres = [Verre allWithOrder:#{#"date_prise" : #"DESC"}];
_table_view_verres.delegate = self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#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 objects_verres.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [_table_view_verres dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
UILabel *lbl_nom = (UILabel *)[cell viewWithTag:10];
UILabel *lbl_date = (UILabel *)[cell viewWithTag:11];
NSManagedObject *matches = objects_verres[indexPath.row];
lbl_nom.text = [matches valueForKey:#"nom_alcool"];
lbl_date.text = [[matches valueForKey:#"date_prise"] formattedDateWithFormat:#"HH:mm - dd/MM/yyyy"];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//code to delete
}
}
I would point you to this post: Swipe to Delete not working
Make sure you implement all the necessary methods, and you don't have a unusual view hierarchy/code running on the app delegate.
I vaguely remember an issue with storyboards and tableview controllers that are embedded inside a view controller. Perhaps someone else can shed more light on this.
Related
I'm subclassing UITableView in my app. It's set up to be it's own delegate.
#interface TableView : UITableView <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, assign) id delegate;
- (id)initWithFrame:(CGRect)frame;
#end
#implementation TableView
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0, self.frame.size.height-144, self.frame.size.width, 40)];
super.delegate = self;
super.dataSource = self;
self.tableFooterView = footerView;
}
return self;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 15;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self.delegate tableView:tableView cellForRowAtIndexPath:indexPath];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog(#"Selected row!");
}
#end
Now, what I don't understand is how I TableView can be the delegate of UITableView, but also have a different delegate property that it pipes some functions to sometimes. So, I'd like for - for example - numberOfRowsInSection to be handled by this class, but for - for example - didSelectRowAtIndexPath to be forwarded to the UIViewController or whatever's presenting it.
Each of the delegate methods have a property (UITableView *)tableView you can use this to identify which table views action to be performed
Eg lets say you have 2 table views tableView1 & tableView2 now do something like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if tableView == self {
return [self.delegate tableView:tableView cellForRowAtIndexPath:indexPath];
} else if tableView == tableView2 {
// Do something
}
}
You can do the same concept using super and self calls
EDIT
Create a property called customDelegate, now in your ViewController set customDelegate = self and keep the TableView's delegate same
Now when you wish the class should handle the call just don't do anything as the behaves would be default
But if you wish that your viewController should handle the call then just pipe it using that customDelegate property
Eg
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if <SOME_CONDITION> {
// This will cause the TableView's delegate to be called
return [self.delegate tableView:tableView cellForRowAtIndexPath:indexPath];
} else {
// We wish the ViewController to handler this action then
return [self.customDelegate tableView:tableView cellForRowAtIndexPath:indexPath];
}
}
I have two UIViews in my app.
in first view there is a tableveiw with two cells(to select city and country). when user select first cell(to select city), then it goes to anothrview that has a list of cities. then when user select a city(select a tableviecell text), the selected should display in firtview's tableviewcell text.
this is my code in secondview controller(it is a tableview Controller).
- (void)viewDidLoad {
[super viewDidLoad];
detailFlights = #[#"colombo1",#"colombo2",#"colombo3",#"colombo14",#"colombo15",#"colombo16",#"colombo17"];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [detailFlights count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"identi" forIndexPath:indexPath];
cell.textLabel.text = [detailFlights objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
This kind of problem is solved using the delegate pattern. A pattern widely used in iOS programming. See this question if you don't know how it works.
As deadbeef said, you must implement delegate method to transfer data to first view, but logic for that is to choose objectAtIndex of indexPath.row in didSelect: [detailFlights objectAtIndex:indexPath.row];
As an alternative to the delegate method that others have mentioned, you can use an unwind segue to pass data back to the previous view controller.
Unwind segues give you a way to "unwind" the navigation stack back through push, modal, popover, and other types of segues. You use unwind segues to "go back" one or more steps in your navigation hierarchy. Unlike a normal segue, which create a new instance of their destination view controller and transitions to it, an unwind segue transitions to an existing view controller in your navigation hierarchy. Callbacks are provided to both the source and destination view controller before the transition begins. You can use these callbacks to pass data between the view controllers.
Here's a example where the second (source) view controller presented a list of fonts, and the first (destination) view controller updates its table row to show the selected font:
- (IBAction)unwindFromFontPreference:(UIStoryboardSegue *)segue
{
FontTableViewController *fontTableViewController = segue.sourceViewController;
if (self.currentFontSelection != fontTableViewController.currentFontSelection)
{
self.currentFontSelection = fontTableViewController.currentFontSelection;
[self.tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:0 inSection:LALSettingsTableViewSectionFont]]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
#interface FirstViewController ()<UITableViewDelegate, UITableViewDataSource, ViewControllerDelegate>
#property (nonatomic, retain) NSMutableArray* data;
#property (nonatomic, retain) IBOutlet UITableView* tableView;
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.data = [NSMutableArray array];
[self.data addObject:#"country"];
[self.data addObject:#"city"];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.data.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
NSString* text = [self.data objectAtIndex:indexPath.row];
cell.textLabel.text = text;
return cell;
}
-(void) updateText:(NSString *)text
{
[self.data replaceObjectAtIndex:1 withObject:text];
[self.tableView reloadData];
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"secondView"])
{
UINavigationController* controller = [segue destinationViewController];
NSArray *viewControllers = controller.viewControllers;
SecondViewController* viewController = [viewControllers objectAtIndex:0];
viewController.delegate = self;
}
}
SecondViewController.h
#import <UIKit/UIKit.h>
#protocol ViewControllerDelegate <NSObject>
-(void) updateText:(NSString*)text;
#end
#interface SecondViewController : UIViewController
#property (nonatomic, assign) id<ViewControllerDelegate> delegate;
#end
SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#property(nonatomic ,retain) NSArray* detailFlights;
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.detailFlights = #[#"colombo1",#"colombo2",#"colombo3",#"colombo14",#"colombo15",#"colombo16",#"colombo17"];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.detailFlights count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
cell.textLabel.text = [self.detailFlights objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{ NSString* text = [self.detailFlights objectAtIndex:indexPath.row];
[self.delegate updateText:text];
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
whole project is here
In didDeselectRowAtIndexPath method you can get that string using following way
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *selectedString = [tableView cellForRowAtIndexPath:indexPath].textLabel.text;
}
now save to pass previous view controller you can store it in UserDefaults OR
make a property in previousViewController and before go back set it value like follow
if you are using navigationController then
ViewController *previousViewController = (ViewController *) self.navigationController.viewControllers[self.navigationController.viewControllers.count-2];
previousViewController.selectedString = selectedString;
or
you can also use block for passing data. example of block
I currently have a MatchCenterViewController that I want to programmatically turn into a UITableViewController. I've attempted to do so below based on tutorials I've found, but it doesn't seem to be appearing.
MatchCenterViewController.m:
#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>
#interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>
#end
#implementation MatchCenterViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"newFriendCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"newFriendCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//etc.
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
#end
As a minimum, you need to implement the following methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
And you need to set the delegate and datasource, typically in viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.dataSource = self;
self.tableView.delegate = self;
}
Also, you need an IBOutlet to the table view if the table view was created in storyboard, or a property for the table view, if the table view was created in code.
I am creating an UITableView and has a question on how to redirect the user to a new view when the person clicks on the cell. It would helpful if could provide some code and possible an explanation. Thank you :)
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
IBOutlet UIButton *Startbutton;
}
#property (strong,nonatomic) NSArray *array;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Startbutton.layer.cornerRadius = 5; // this value vary as per your desire
Startbutton.clipsToBounds = YES;
//Status Bar
[self setNeedsStatusBarAppearanceUpdate];
//Array
self.array = [[ NSArray alloc]initWithObjects:#"1",#"2",#"3", nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
//Array Main Code
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.array.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
cell.textLabel.text = [self.array objectAtIndex:indexPath.row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
#end
There is a pretty basic way of doing this without having to write code at all, but I am not sure if it is exactly what you want, sorry if it doesn't help.
Drag in a UITableViewController, in the Attributes inspector, select Static Cells from the 'Content' drop down box. Then add how ever many cells you like, click on the cell, then under the attributes inspector change the 'Style' to whatever you like and then change the content of the cell. Then all you have to do to link that cell to a new view is; right click the cell and drag your cursor to the destination view, then Select Modal (or Push if you are in a navigation controller).
That way when you run the app and click on that cell you should be switched to the new view.
No coding is required at all.
Hoped that helped in someway.
Cheers
I swipe on a table view cell to reveal the delete-confirmation button. But once I lift my finger, showingDeleteConfirmation gives NO even if the button is still there. (It does give YES when the button is showing up, I mean, before I lift my finger.) Am I missing something, or it's a real bug in iOS?
Following is my test code. (Copy&paste it into ViewController.m of a single-view project and run it in the simulator, and shift+command+M to trigger the NSLog.)
#import "ViewController.h"
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#end
#implementation ViewController {
UITableViewCell *_myCell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 100, 320, 200)];
myTableView.dataSource = self;
myTableView.delegate = self;
[self.view addSubview:myTableView];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
_myCell = [[UITableViewCell alloc] init];
_myCell.contentView.backgroundColor = [UIColor greenColor];
return _myCell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
NSLog(#"_myCell.showingDeleteConfirmation = %#", _myCell.showingDeleteConfirmation ? #"YES" : #"NO");
}
#end
According to documentation:
Returns whether the cell is currently showing the delete-confirmation button. (read-only)
...
When users tap the deletion control (the red circle to the left of the cell), the cell displays a "Delete" button on the right side of the cell
So first you need to make grid editable
[myTableView setEditing:YES];
then user taps circle to delete (they not able to swipe left in this mode), and voilĂ :
_myCell.showingDeleteConfirmation = YES
While you can expect to use it as you described in your question, they don't promise it.