I have an exception like this:
Thread 1: EXC_Breakpoint (code = EXC_I386_BPT,subcode=0x0)
In my story board i have 3 controllers. Navigation controller, UIViewController and then a tableviewcontroller. When the app first launches UIViewController is shown. I add some stuff to the Core Data DB. Then on this controller itself i have a "Check Records" bar button. I click that and move to the 3rd controller the tableviewcontroller. Here i can see the records that i added on that day. I click the one i just added and traverse back to the UIviewcontroller screen using NSNotifications like this:
//This code is in tableviewcontroller.didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ITMAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.salesOrderObject = [self.salesOrdersArray objectAtIndex:indexPath.row];
NSNotification *notification =[NSNotification notificationWithName:#"reloadRequest"
object:self];
[[NSNotificationCenter defaultCenter] postNotification : notification];
[self.navigationController popViewControllerAnimated:YES];
}
In my ViewController viewDidLoad i write this code to listen to the notification.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(newReloadData)
name:#"reloadRequest"
object:nil];
-(void)newReloadData
{
self.salesOrder = self.appDelegate.salesOrderObject; //self.reloadedSOObject; //appDelegate.salesOrderObject;
if(self.salesOrder !=nil)
{
//I add UItextviews,textfields,buttons etc here to a scrollview.
ITMCustomView *cView = [[ITMCustomView alloc] initWithFrame:CGRectMake(187, 660, 400, 400)
andOrderedItem:#"New"
andTag:self.tagNumber
withFoldArray:self.foldTypeArray
withRollArray:self.rollTypeArray];
cView.tag =self.tagNumber;
NSLog(#"Assigned tag is %d",cView.tag);
cView.delegate = self;
//[self.scrollView addSubview:customView];
//self.scrollView.contentSize = CGRectMake(187, 660, 400, 400).size;
CGPoint scrollPoint = CGPointMake(0.0, (cView.frame.origin.y/500)*400);
[self.scrollView setContentOffset:scrollPoint animated:YES];
[self.scrollView addSubview:cView];
CGFloat scrollViewHeight = 0.0f;
for (UIView *view in self.scrollView.subviews)
{
scrollViewHeight += view.frame.size.height;
}
CGSize newSize = CGSizeMake(320,scrollViewHeight);
//CGRect newFrame = (CGRect){CGPointZero,newSize};
[self.scrollView setContentSize:newSize];
NSLog(#"750 the tag number is %d",self.tagNumber);
NSLog(#"the scroll view height is %f",scrollViewHeight);
self.currentCGRectLocation = cView.frame;
}
}
I am thinking if the exception occurs at adding something to scroll view.Or on looking some more on stack overflow it could be either because of delegate or NSnotification. I cant figure out why and where the exception occurs.Does writing this line of code give me this exception?
[self.navigationController popViewControllerAnimated:YES];
this question is extension of this question
Adding Customview to UIScrollview results in stuck screen when Scrolling
I have no idea where/how its getting this exception. If you need more info please ask. Thanks...
One thing might be when you call newReloadData you notify notification center that what class has#"reloadRequest" string with newReloadData method should run it, so you need to first pop the view then call the notification
Just try change the line order first call [self.navigationController popViewControllerAnimated:YES]; then call [[NSNotificationCenter defaultCenter] postNotification : notification];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ITMAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.salesOrderObject = [self.salesOrdersArray objectAtIndex:indexPath.row];
NSNotification *notification =[NSNotification notificationWithName:#"reloadRequest"
object:self];
//here first pop view
[self.navigationController popViewControllerAnimated:YES];
//then send notification before scopes end
[[NSNotificationCenter defaultCenter] postNotification : notification];
}
Related
I have a Tableview in inside a viewcontroller. I have added following code to get keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleKeyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleKeyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
And on keyboard show i am scrolling my table to bottom.
- (void)handleKeyboardWillShow:(NSNotification *)notification
{
[self scrollToBottomAnimated:YES];
}
But i have a textview in my view controller as well. So when i click on textview the handleKeyboardWillShow method is called as well resulting unnecessary scrolling my tableview which i do not need if textview is clicked.
Can some one please help me figure out how to detect from which sender handleKeyboardWillShow is called.
Thanks
You can do it by checking who is first responder.
- (void)handleKeyboardWillShow:(NSNotification *)notification
{
if ([textFieldForScrolling isFirstResponder]) {
[self scrollToBottomAnimated:YES];
} else {
NSLog(#"Is a different text input");
}
}
Let me know if you need more explanation.
I would register for keyboardWillChange - which covers both showing and hiding. You can get the keyboard rect and adjust your content offset based on the keyboard's location. Note: You can animate the change in content offset - I just didn't do it in this answer.
In your textfield delegate methods willBeginEditing and didEndEditing, you can set the state variable called currentTextField.
-(void)keyboardWillChange:(NSNotification *)notification {
keyboardRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
CGPoint currentFieldPoint = [currentTextField convertPoint:currentTextField.frame.origin toView:self.view];
if(keyboardRect.origin.y < currentFieldPoint.y + currentTextField.frame.size.height){
//move stuff here
[[self tableView] setContentOffset:CGPointMake(0, [self tableView].contentOffset.y + offsetValue)];
}
}
I'm trying to make my timer "run" in the background by saving the time to disk on entering background and retrieving it on entering foreground. Each view controller has a timer and a timeInterval specified by the user. The problem is, I don't know how to access the timeInterval variable. I think I can get the difference of time by using something like this (would this work?):
NSTimeInterval idleTime = [dateReturnedToForeground timeIntervalSinceDate:dateEnteredBackground];
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
elapsedTime -= idleTime;
Each view controller (and timer/time interval) is accessed like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC;
if (![self.detailViewsDictionary.allKeys containsObject:indexPath]){
detailVC = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
[self.detailViewsDictionary setObject:detailVC forKey:indexPath];
detailVC.context = self.managedObjectContext;
}else{
detailVC = self.detailViewsDictionary[indexPath];
}
Tasks *task = [[self fetchedResultsController] objectAtIndexPath:indexPath];
detailVC.testTask = task;
[[self navigationController] pushViewController:detailVC animated:YES];
NSLog(#"%#", self.detailViewsDictionary);
[[NSNotificationCenter defaultCenter] addObserver:detailVC forKeyPath:self.detailViewsDictionary[indexPath] options:nil context:nil];
}
I am adding each view controller to the Notification Center so it can be accessed in the app delegate (i think this is right?). Problem is, I don't know how to combine the first code with the view controller code, because I can't access the view controller's properties in the app delegate. Any suggestions so that I can make my timer "run" in the background?
You are going about this all wrong. There is no need to do any of this in the app delegate.
Have each view controller listen for the UIApplicationWillResignActiveNotification notification. Then each view controller can do whatever it feels is appropriate to save its data when the notification is received.
Update:
In the view controller's init... method, register for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(resigningActive) name:UIApplicationWillResignActiveNotification object:nil];
In the view controller's dealloc method, unregister:
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
}
Then implement the resigningActive method:
- (void)resigningActive {
// The app is resigning active - do whatever this view controller needs to do
}
I'm using Storyboards and iOS 6.1, I'm initiating a view like this:
PropertiesViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"Properties"];
PropertiesViewController has a 60px height viewn an including some labels and more. I want to add these views to another ScrollView as subview. Here what I'm doing:
controller.leftLabel.text = #"Test";
controller.background.image = [UIImage imageNamed: #"someimage.png"];
controller.view.frame = CGRectMake(0, currentHeight, controller.view.frame.size.width, cellHeight);
[self.scrollView addSubview: controller.view];
leftLabel is a UILabel and backgorund is a UIImageView.The problem is none of view's elements are not updating outside of PropertiesViewController. addSubview: is just adding as how its created, not allow to configure.
I've already checked NSNotificationCenter approach, if I'm not wrong it's not about updating instances. And I've also already added a method to receiver class to update labels inside of PropertiesViewController. That even did not worked.
What am I doing wrong?
Note: You may ask that why just I'm not using a TableView. There has to be more dynamic resources and their besides are not clear. There is also one thing that TableView is not possible with ScrollView, in some cases I've never use a TableView so scrollview will manage scroll.
Any help would be great.
EDIT:
According to Kalpesh's answer here what I did:
In my sender view controller:
PropertiesViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"Properties"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"update" object:#"Test string"];
controller.view.frame = CGRectMake(0, currentHeight, controller.view.frame.size.width, cellHeight);
// not sure how but I can change frame successfully.
[self.scrollView addSubview: controller.view];
Here is receiver:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Called"); // Working fine
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changetext:) name:#"update" object:nil];
//even if set some object, nothing changed
}
- (void) changetext:(NSNotification *)notification {
NSLog(#"Received"); // Not working
leftLabel.text = [notification object];
}
Try this, You have to send notification from other viewcontroller when you want to change label of text
..
[[NSNotificationCenter defaultCenter] postNotificationName:#"changetext" object:stringobj];
in mainviewcontroller
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changetext:) name:#"changetext" object:nil];
}
- (void) changetext:(NSNotification *)notification
{
NSString * text =[notification object];
lbl.text=text;
}
I have a Tableview where the user can enter values into a textField as one of the custom cells
Apple have some documentation about how to adjust view content by repositioning the view clear of the keyboard's vertical dimension ( Here ) but it relies upon one placing that view into a UIScrollView. I cant do this with a tableview.
I could redesign the app so that the entry gets done in a separate detail view using the usual navigation controller, but i'd rather the user not have to perform an extra touch ( and be ferried off into yet another screen ) if possible. I like the idea of doing the deed "right where we are"
so my workaround to have a few extra tableview cells at the bottom containing a %20 or so, normal usage shouldn't register the oddity, as they are only focussed on what is visible.
I'd have to store the spaces in my datasource array and then sort descending, but that's OK
the question is, is this good practice? and even more possibly, could it be against Apple's HIG sufficient for refusal?
UITableView is a subclass of UIScrollView, so should be able to adjust the content and scroll view insets just like in the example you linked.
The way I've solved this issue is to subclass UITableView. Here's what I've done:
// AOTableView.h file
typedef enum
{
AOKeyboardStateUnknown = 0,
AOKeyboardStateShowing,
AOKeyboardStateHidden
} AOKeyboardState;
#import <UIKit/UIKit.h>
#import "AOKeyboardState.h"
#interface AOTableView : UITableView
#property (nonatomic) BOOL observeKeyboardNotifications;
#property (nonatomic) AOKeyboardState keyboardState;
#end
// AOTableView.m file
#import "AOTableView.h"
#interface AOTableView(Private)
#property (nonatomic) CGRect frame0;
- (void)setup;
- (void)keyboardWillShow:(NSNotification *)notification;
- (void)keyboardWillHide:(NSNotification *)notification;
#end
#implementation AOTableView
#pragma mark - Object lifecycle
- (void)awakeFromNib
{
[self setup];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setup];
}
return self;
}
- (void)setup
{
self.contentSize = self.frame.size;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_keyboardState = AOKeyboardStateUnknown;
_frame0 = self.frame;
_observeKeyboardNotifications = NO;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Custom setters
- (void)setObserveKeyboardNotifications:(BOOL)observeKeyboardNotifications
{
if (_observeKeyboardNotifications == observeKeyboardNotifications)
return;
_observeKeyboardNotifications = observeKeyboardNotifications;
if (_observeKeyboardNotifications)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
else
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
}
#pragma mark - UIKeyboard Notifications
- (void)keyboardWillShow:(NSNotification *)notification
{
if (self.keyboardState == AOKeyboardStateShowing)
return;
self.frame0 = self.frame;
self.keyboardState = AOKeyboardStateShowing;
NSDictionary* info = [notification userInfo];
CGRect keyboardFrame = CGRectZero;
[[info objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrame];
CGRect frame = self.frame0;
frame.size.height = CGRectGetMinY(keyboardFrame) - CGRectGetMinY(frame);
self.frame = frame;
[self scrollToRowAtIndexPath:self.indexPathForSelectedRow atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
[self deselectRowAtIndexPath:self.indexPathForSelectedRow animated:NO];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
if (self.keyboardState == AOKeyboardStateHidden)
return;
self.keyboardState = AOKeyboardStateHidden;
self.frame = self.frame0;
}
#end
After creation (or loading the view from an IBOutlet), you call this method to tell the class to start listening for keyboard notifications:
[tableViewInstance setObserveKeyboardNotifications:YES];
Whenever a user clicks on a cell, it becomes the self.indexPathForSelectedRow cell... so its scrolled to by the AOTableView instance automatically.
For this to work, though, I've had to turn off userInteraction on the UITextField within the cell (otherwise, the device can get confused about if the user is clicking on the cell or on the text field). Instead, when a user selects a cell that has a text field, I tell the text field to the become first responder, like this:
[cell.textField becomeFirstResponder];
I hope this helps.
You don't need the extra cells or anything fancy.
Since your text fields are inside the table view cells, you can use the following:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
UITableViewCell *cell = (UITableViewCell *)textField.superview.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
return YES;
}
This means that the keyboard will scroll appropriately each time a text field becomes first responder. This takes advantage of the table view being a scroll view subclass.
Note that this assumes:
Your (table) view controller is the text fields' delegate.
Your text field is a subview of the cell's content view, not the cell itself.
If the text field is a subview of the cell, the first line of the method above should reference only one superview (i.e., textField.superview).
I have an app where I add a new item to a table view by having the user tap an edit button which shows a textfield cell at the bottom of the table, similar to the built in Notifications app. I need to adjust the table when the keyboard is shown so that it is not obstructed when their are many rows in the table. I am doing this by subscribing to the notification for when the keyboard shows:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector (keyboardDidShow:)
name: UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector (keyboardDidHide:)
name: UIKeyboardDidHideNotification
object:nil];
}
...
...
-(void) keyboardDidShow: (NSNotification *)notif
{
// If keyboard is visible, return
if (self.keyboardVisible)
{
return;
}
// Get the size of the keyboard.
NSDictionary* info = [notif userInfo];
NSValue* aValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Adjust the table view by the keyboards height.
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
NSIndexPath *path = [NSIndexPath indexPathForRow:self.newsFeeds.count inSection:0];
[self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
self.keyboardVisible = YES;
}
However, the table that I let a user add a row to can also be tapped and a new view is pushed on to the app. This view also has a text view and when the user taps in it and the keyboard shows the first viewcontroller still gets the notification, which causes a crash.
How can I either ignore the notification or get it to not fire when a new view is pushed?
You could add the class as an observer in viewDidAppear and remove it in viewWillDisappear.