I am working on app and simply editing the cell text field and at finish editing, I am trying to update coredata but app get stack and some time it give SIGSTOP and sometime it says unable to allocate more memory kinda error, please help me with this.
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if ((textField.tag - 999) > 0) {
NSArray *visible = [self.productTable indexPathsForVisibleRows];
UITableViewCell* cell = [self.productTable cellForRowAtIndexPath:[visible objectAtIndex:(textField.tag - 999)]];
for (UIView* subview in [cell.contentView subviews]) {
if ([subview isKindOfClass:[UITextField class]]) {
textField = (UITextField*)subview;
Product* myProduct = (Product*)[self.productArray objectAtIndex:(textField.tag - 1000)];
[myProduct setValue:[NSNumber numberWithFloat:[textField.text floatValue]] forKey:#"quantity"];
[myProduct setValue:[NSNumber numberWithFloat:[myProduct.quantity floatValue] * [myProduct.rate floatValue]] forKey:#"amount"];
NSLog(#"Product %#", myProduct);
NSError *error; if (![self.managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
}
}
It seems that your subview loop is really not necessary.
But already your check by visible cells seems flawed.
You get the index paths of all visible cells.
You pick one of these index paths based on the text field's tag.
--> This has unpredictable results. If the table view has scrolled, the exact index of your cell in the array of visible cells is not clear. If the cell has scrolled off the screen, the array will be empty and you get a crash.
Why so complicated?
Isn't the text field you need exactly the text field that is passed into the delegate method?
All you need is this:
UITableViewCell *cell = (UITableViewCell*) textField.superview.superview;
Product *myProduct = [self.fetchedResultsController objectAtIndexPath:
[self.tableView indexPathForCell:cell]];
You are using a NSFetchedResultsController, right? If not, you definitely should!
Related
I am new in IOS. And sorry for my week english, That time facing a problem. My problem is that I am using a CoreData in my project. In my project TableViewCell scroll left to right. Swipe is proper but I want to That cell are swiped and change database value. Particular indexpath.row are swipe and same row content change in DataBase table.
TableViewCell swipe by SWTableViewCell This Tutorial
- (void)swipeableTableViewCell:(SWTableViewCell *)cell scrollingToState:(SWCellState)state{
if(state ==1){
NSLog(#"animation start");
}
else
NSLog(#"animation end");
}
I am so tried many days but do not update DataBase table in same row on TableViewCell swipe, How can i do this, please help, ThankYou
Update answer
- (void)swipeableTableViewCell:(SWTableViewCell *)cell scrollingToState:(SWCellState)state{
if(state ==1){
NSIndexPath *indexPaths = [self.table_view indexPathForCell:cell];
NSManagedObject *selectedDevices = [self.devices objectAtIndex:[self.table_view indexPathForCell:cell].row];
NSLog(#"device %#",selectedDevices);
[selectedDevices setValue:#"OnTime" forKeyPath:#"time"];
NSError *saveError = nil;
[self.managedObjectContext save:&saveError];
if (saveError)
{
}
else
{
}
}
Based on the discussion in the comments you need to do follow these simple steps to achieve what you want
Set the tag of the cell in cellForRowAtIndexPath: method i.e. cell.tag = indexPath.row so that you can get the record from devices array later
In the delegate method of cell access the relevant NSManagedObject and update the value of time
Save these changes in the persistent store
Reload the cell at indexPath you just updated
The code would be something like:
NSInteger index = cell.tag;
NSManagedObject *device = [self.devices objectAtIndex:index];
[device setValue:[NSDate date] forKey:#"tine"];
NSError *error;
if ([context save:&error]) {
[self.tableView reloadRowsAtIndexPaths:[NSIndexPath indexPathForRow:index inSection:0] withRowAnimation:UITableViewRowAnimationAutomatic];
}
This is my error:
-[__NSArrayM objectAtIndex:]: index 12 beyond bounds for empty array
I know this error means I'm trying to access an "empty array".
This error only happens in viewX when it is popped back from viewY. When you press 'back button' on navigation bar in viewY and scroll the tableView immediately, it will crash and cause this error. I am using the RETableViewManager to load my tableView.
In viewX's viewDidLoad:
[[RACSignal combineLatest:#[RACObserve(self, record), RACObserve(self, restaurant)]] subscribeNext:^(id x) {
[self setupItems];
}];
in setupItems:
RecordManager *recordManager = [[EZRecordManager alloc] initWithRecord:self.record restaurant:self.restaurant sender:self.navigationController];
self.items = [recordManager items];
self.section = [RETableViewSection section];
[self.items each:^(id data) {
if ([data isKindOfClass:[NSString class]]) {
self.navigationItem.title = (NSString *)data;
} else {
[self registerItem:[data class]];
[self.section addItem:data];
}
}];
[self.manager addSection:self.section];
[self.tableView reloadData];
I NSLogged my array 'self.items'. and this is what logs according to the method:
viewDidAppear - (
"\U5df2\U8a02\U4f4d\Uff0c\U5c1a\U672a\U7528\U9910",
"<REReservationHeaderItem: 0x14015b0b0>",
"<REAttributedStrItem: 0x14015b1b0>",
"<REAttributedStrWithNextItem: 0x140191a70>",
"<REAttributedStrItem: 0x140193f60>",
"<RESpacerItem: 0x140194870>",
"<REAttributedStrWithNextItem: 0x14019ce10>",
"<REAttributedStrItem: 0x140199230>",
"<RESpacerItem: 0x1401a04e0>",
"<REActionItem: 0x14019e490>",
)
The NSLog logs the same array in setupItems so I know the array is still there because self.item is saved as a property:
#property (nonatomic, strong) NSArray *items;
So this algorithm works as expected when I'm loading viewX for the first time, but as soon as I go into another view(viewY) and press the 'back button' on viewY to pop to viewX and then immediately scroll, it crashes with the above error. If I wait for a second (maybe even half a second), viewX will work properly and have no issue. I know this is minor but my PM is stressing that this shouldn't happen. How can I solve this problem?
The method the error occurs in (part of the RETableViewManager library):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section];
RETableViewItem *item = [section.items objectAtIndex:indexPath.row];
UITableViewCellStyle cellStyle = UITableViewCellStyleDefault;
if ([item isKindOfClass:[RETableViewItem class]])
cellStyle = ((RETableViewItem *)item).style;
NSString *cellIdentifier = [NSString stringWithFormat:#"RETableViewManager_%#_%li", [item class], (long) cellStyle];
Class cellClass = [self classForCellAtIndexPath:indexPath];
if (self.registeredXIBs[NSStringFromClass(cellClass)]) {
cellIdentifier = self.registeredXIBs[NSStringFromClass(cellClass)];
}
if ([item respondsToSelector:#selector(cellIdentifier)] && item.cellIdentifier) {
cellIdentifier = item.cellIdentifier;
}
RETableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
void (^loadCell)(RETableViewCell *cell) = ^(RETableViewCell *cell) {
cell.tableViewManager = self;
// RETableViewManagerDelegate
//
if ([self.delegate conformsToProtocol:#protocol(RETableViewManagerDelegate)] && [self.delegate respondsToSelector:#selector(tableView:willLoadCell:forRowAtIndexPath:)])
[self.delegate tableView:tableView willLoadCell:cell forRowAtIndexPath:indexPath];
[cell cellDidLoad];
// RETableViewManagerDelegate
//
if ([self.delegate conformsToProtocol:#protocol(RETableViewManagerDelegate)] && [self.delegate respondsToSelector:#selector(tableView:didLoadCell:forRowAtIndexPath:)])
[self.delegate tableView:tableView didLoadCell:cell forRowAtIndexPath:indexPath];
};
if (cell == nil) {
cell = [[cellClass alloc] initWithStyle:cellStyle reuseIdentifier:cellIdentifier];
loadCell(cell);
}
if ([cell isKindOfClass:[RETableViewCell class]] && [cell respondsToSelector:#selector(loaded)] && !cell.loaded) {
loadCell(cell);
}
cell.rowIndex = indexPath.row;
cell.sectionIndex = indexPath.section;
cell.parentTableView = tableView;
cell.section = section;
cell.item = item;
cell.detailTextLabel.text = nil;
if ([item isKindOfClass:[RETableViewItem class]])
cell.detailTextLabel.text = ((RETableViewItem *)item).detailLabelText;
[cell cellWillAppear];
return cell;
}
Usually when "waiting a little fixes the problem", it's because you have an async problem.
Something to check first :
Make sure your reload code is called when you move back. Maybe your tableview didn't get emptied, but the array did. Moving back would let you scroll the old content (still loaded) but the delegate methods won't be able to create new cells because the array is now empty.
If you wait, your async method does it's job and the array is now full again, which makes everything work fine.
Possible solution :
Empty then reload the tableview in viewWillAppear. This will cause a visual flash of the tableview going empty and then full again. It will also scroll you to the first element. That being said, it's really easy and fast, and with a spinner it will appear much smoother.
Other possible solution :
Keep the data loaded after leaving the page, so when you come back it's still there. You can use anything that will keep the data loaded while in the app. It could be a singleton class that stays instantiated, or save in a database and reload from it (it's much faster than straight up loading from the internet), or anything that you can think of.
Background: I'm working on my first iOS app that uses a UITableView for data being loaded dynamically from a web source. No, this is not homework.
Problem: When I scroll down the list bounces back to the top.
Things I've Tried:
This suggestion (reducing the size of the table view) works but now I have large space at the bottom of the screen.
I've tried using [tableView reloadData] but this doesn't changing the behavior. I even hooked this up to a button to make sure it was firing after the view was populated.
I also read a few post about contentSize and how to calculate based on the amount of data loaded in the table. This looks like the solution I need but am having trouble getting a clear explanation of how to implement it for my setup.
Code:
I started a new project with single view application template and added a UITableView to the default view in the main storyboard.
In view controller I have the following (relevant) code:
#interface ERViewController ()
#property (nonatomic, strong) NSMutableArray *myArray;
#property (nonatomic,retain) UITableView *tableView;
#end
#implementation ERViewController
{
NSMutableArray *tableData;
}
Here I am loading the data from Parse.com into my table and then calling [self loadView] to get the view to update after the query finishes.
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *returnedData = [[NSMutableArray alloc] init];
PFQuery *query = [PFQuery queryWithClassName:#"Persons"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
//NSLog(#"display name: %#",object[#"firstName"]);
[returnedData addObject:[NSString stringWithFormat:#"%#, %#", object[#"lastName"],object[#"firstName"]]];
}
tableData = returnedData;
// Reload view to display data
[self loadView];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (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];
}
// Configure the cell
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
NSArray *itemArray = [NSArray arrayWithObjects: #"✔︎", #"?", #"X", nil];
UISegmentedControl *mainSegment = [[UISegmentedControl alloc] initWithItems:itemArray];
mainSegment.frame = CGRectMake(200,0, 100, 43);
self.navigationItem.titleView = mainSegment;
mainSegment.selectedSegmentIndex = [itemArray count];
[mainSegment addTarget:self
action:#selector(mainSegmentControl:)
forControlEvents: UIControlEventValueChanged];
mainSegment.tag = indexPath.row;
[cell.contentView addSubview:mainSegment];
return cell;
}
Sample screen shot:
Since I'm waiting for the data to load I'm not sure where to resize the view and how exactly I need to resize the view.
You should never ever manually call loadView.
Once you have your tableData call reloadData on the tableView.
You should also load you table data in viewDidAppear:, and if you only want to do it once, create a flag to see if its already been loaded. While waiting for the data to load, tableData will be nil so if you query the count of it, you will be messaging a nil object and therefore it would act like returning 0.
Finally you should not be touching the contentSize of the table view, it handles that itself. You just need to make sure that in Interface Builder, the table view's frame is the same as the root views frame.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSMutableArray *returnedData = [[NSMutableArray alloc] init];
PFQuery *query = [PFQuery queryWithClassName:#"Persons"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
//NSLog(#"display name: %#",object[#"firstName"]);
[returnedData addObject:[NSString stringWithFormat:#"%#, %#", object[#"lastName"],object[#"firstName"]]];
}
tableData = returnedData;
// Reload view to display data
[self.tableView reloadData];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
If you just want a UIViewController with only a UITableView in it, consider using a UITableViewController.
Posted for others dealing with the same problem I had and can't just move to a different view controller.
To solve the problem with a UITableView within a general view controller (not UITableViewController) click the little icon below the view controller that looks like this:
The button is titled "Resolve Auto Layout Issues"
Select "Reset to suggested constraints in View Controller"
This fixed all my scrolling issues since I must have been incorrectly snapping to the auto layout edges.
So, simply put, I have the following setup:
UIViewController - this is the main, parentController
Within this main view, I have a UIScrollView at the very top, and a UITableView below it.
The UITableView is populated with core data via NSFetchedResultsController...
UIScrollView (The UIViewController is the delegate of this UIScrollView)
|- Within this, I have 2 UIViews that I show: 1 contains a UISegmentedControl, the other just contains nothing but UILabels
When the segmentedControl's value changes (ie: A user clicks on it), I just want to reload the UITableView's list of rows by issuing a new fetchRequest (by just changing the predicate and refetching) to the NSFetchedResultsController.
However, that's exactly where I began having an issue.
Whenever I issue [tableView reloadData], the top-most UIScrollView (not the UITableView's ScrollView, but the extra one I added to the UIViewController) stop responding.
Before I ever issue [tableView reloadData], scrolling works just fine. After I issue it, the scrollView stops responding, and scrolling stops completely.
I've tried all of the following:
[self.topScrollView setScrollingEnabled:YES]
self.topScrollView.delegate = self;
[self.topScrollView setContentSize:CGSizeMake(640.0f, self.topScrollView.frame.size.height)];
[self.topScrollView setContentOffset:CGPointMake(0, 0)];
I removed the UISegmentedControl completely. What I did instead was put a button at the very top of the view, and just issued [tableView reloadData];. It is when the tableView is reloaded, that the UIScrollView stops responding.
Is this a bug? Am I completely missing something here? The only thing I can even begin to think of is that the UITableView inherits from UIScrollView, and so that when the table reloads, it must be doing something to cause the topScrollView to either lose all sense of contentOffset/Size and/or its delegate or something...
Has anyone ever tried this? Has anyone ever had this issue?
The only way I could solve this was by creating a ContainerView, splitting the header out into its own UIViewController, and then setting that UIViewController as the UIVScrolLView delegate and just handling the entire header there.
While this works - I'm kind of confused, and would really like to know if a better alternative exists...
Edit: Here is a basic code example of what I was doing before I split it out the header into a ContainerView and just loaded it via embededSegue:
-(void)viewDidLoad {
[super viewDidLoad];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.topScrollView setContentSize:CGSizeMake(640.0f, self.topScrollView.frame.size.height)];
[self.topScrollView setContentOffset:CGPointMake(0, 0)];
}
// .....
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"listItemCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Load the cell with what is in core data at this index path..
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
#pragma mark SegmentedControl Switch
- (IBAction)segmentedControlValueChanged {
NSFetchRequest *aRequest = [[self fetchedResultsController] fetchRequest];
NSPredicate *predicate;
switch(self.segmentedControl.selectedSegmentIndex) {
case 0:
predicate = [NSPredicate predicateWithFormat:#"status = %#", #"incomplete"];
break;
case 1:
predicate = [NSPredicate predicateWithFormat:#"status = %#", #"complete"];
break;
case 2:
predicate = [NSPredicate predicateWithFormat:#"status = %#", #"deleted"];
break;
default:
predicate = [NSPredicate predicateWithFormat:#"status = %#", #"none"];
break;
}
[aRequest setPredicate:predicate];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[self.tableView reloadData];
// self.topScrollView.delegate = self;
// [self.topScrollView setContentSize:CGSizeMake(640.0f, self.topScrollView.frame.size.height)];
// [self.topScrollView setContentOffset:CGPointMake(0, 0)];
// [self.topScrollView setScrollEnabled:YES];
}
I have a Table view controller with 5 textboxes in a row which are added with null values when we click on add button.
I am trying to save the data in a database via Core Data.
My problem is that when I click in the text box and finish putting the value and via a touch screen when i am trying to put the data the cursor goes to the other text box but do not allow me to enter text until I press the Return key on the keyboard.
Here is my coding for the text box:
-(void)textFieldDidEndEditing:(UITextField *)textField
{
row1=textField.tag;
NSLog(#"Row is %d",row1);
path = [NSIndexPath indexPathForRow:row1 inSection:0];
Input_Details *inputDetailsObject1=[self.fetchedResultsController objectAtIndexPath:path];
/*
Update the Input Values from the values in the text fields.
*/
EditingTableViewCell *cell;
cell = (EditingTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row1 inSection:0]];
inputDetailsObject1.mh_Up= cell.cell_MH_up.text;
inputDetailsObject1.mh_down=cell.cell_MH_dn.text;
inputDetailsObject1.sewer_No=[NSNumber numberWithInt:[cell.cell_Sewer_No.text intValue]];
inputDetailsObject1.mhup_gl=[NSNumber numberWithFloat:[cell.cell_gl_MHUP.text floatValue]];
inputDetailsObject1.mhdn_gl=[NSNumber numberWithFloat:[cell.cell_gl_MHDN.text floatValue]];
inputDetailsObject1.pop_Ln=[NSNumber numberWithInt:[cell.cell_Line_pop.text intValue]];
inputDetailsObject1.sew_len=[NSNumber numberWithFloat:[cell.cell_Sewer_len.text floatValue]];
[self saveContext];
[textField resignFirstResponder];
NSLog(#"Saving the MH_up value %# is saved in save at index path %d",inputDetailsObject1.mh_Up,row1);
}
-(void)saveContext
{
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSError *error = nil;
if (![context save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
Here is a picture of the table view and text boxes.
didEndEditing means you pressed the return key or moved the responder to another object.
If you want to move to the next text box by some other mechanism then you need to implement some other delegate method. For example, this method textField:shouldChangeCharactersInRange:replacementString: fires every time you change a characters in a textfield. You can use it to change the responder based on the input for example.
To tell the next text box to become the first responder you call [mySecondField becomeFirstResponder].
hope this helps.