I am developing an application with multiple views. I have a template for an image gallery which I have created in a xlib file. This view will be loaded as a single page in a scroll view. I am able to get the view loaded multiple time from xlib with the following:
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (id)initWithFrame:(CGRect)frame
{
self = [[[NSBundle mainBundle] loadNibNamed:#"GSEstimateView" owner:self options:NULL] lastObject];
self.commentText.delegate = self;
self.scrollView.delegate = self;
self.commentText.delegate =self;
[self registerForKeyboardNotifications];
return self;
}
The first issue I am facing is, when the key board is shown the keyboardWasShown: method is getting called for as many UIViews I have created. If I try to load the keyboard from the second UIView, I get an exception for invalid selector being called. Is the UIView loaded from a nib or xlib Singleton? How can I have my UIView instance notified if I load it from nib file?
(^.^)"Hi sorry for my English is not good if someone like correct my redaction I would appreciate this"
Hi first I don't recommend to use NSNotification prefer to use protocols like this.
#protocol KeyBoardDelegate <NSObject>
- (void)KeyBoardVisible:(BOOL)op;
#end
And if you have multiples views and if you want to now the view control like this:
*viewDidLoad, viewDidUnload, viewWillDisappear, viewWillAppear, and others *
I recommend use the view of the UIViewController like this.
UIViewControllerCustom *example = [[UIViewControllerCustom alloc] initWithNibName:#"exampleNIB" bundle:[NSBundle mainBundle]];
[self.view addSubview:example.view];
Using this you can take the control of the view of example viewcontroller and use the methods
- (void)viewDidLoad{
[super viewDidLoad];
//When the nib has been loaded.
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//When the view is show.
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
//The view is hidden
}
- (void)viewDidUnload{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
And some more methods. :)
Related
Sorry for the basic Question, I'm fairly new to programming and trying to understand something in the code apple suggester for a certain solution to something I wanted to preform.
I created a simple notes app, very basic, currently I have:
1. CreateNote view controller
2. NotesList table view controller
So I wanted to add a behaviour when a note is being created and a user types below the keyboard so the text resized so the user can still see what he types and the text is not going behind the keyboard.
So I add some lines of code suggested by apple to accomplish that.
In the viewWillAppear called a method on NSNotificationCenter and I could not understand where is an NSNotificationCenter object is declared...?
So this is my CreateNote view controller(Please help me understand why they could preform this call):
#import "NMCreateNotesViewController.h"
#interface NMCreateNotesViewController () <UITextViewDelegate>
#property (weak, nonatomic) IBOutlet UIBarButtonItem *createButton;
#property (weak, nonatomic) IBOutlet UITextView *textField;
#end
#implementation NMCreateNotesViewController
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// listen for keyboard hide/show notifications so we can properly adjust the table's height
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
#pragma mark - Notifications
- (void)adjustViewForKeyboardReveal:(BOOL)showKeyboard notificationInfo:(NSDictionary *)notificationInfo
{
// the keyboard is showing so resize the table's height
CGRect keyboardRect = [[notificationInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
NSTimeInterval animationDuration =
[[notificationInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect frame = self.textField.frame;
// the keyboard rect's width and height are reversed in landscape
NSInteger adjustDelta = UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? CGRectGetHeight(keyboardRect) : CGRectGetWidth(keyboardRect);
if (showKeyboard)
frame.size.height -= adjustDelta;
else
frame.size.height += adjustDelta;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.textField.frame = frame;
[UIView commitAnimations];
}
- (void)keyboardWillShow:(NSNotification *)aNotification
{
[self adjustViewForKeyboardReveal:YES notificationInfo:[aNotification userInfo]];
}
- (void)keyboardWillHide:(NSNotification *)aNotification
{
[self adjustViewForKeyboardReveal:NO notificationInfo:[aNotification userInfo]];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.createButton) return;
if (self.textField.text.length > 0) {
self.note = [[NMNote alloc] init];
self.note.content = self.textField.text;
}
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Is NSNotificationCenter is part of UITextView?
No it is not. NSNotificationCenter is - as it's name says - a notification center. Objects can subscribe to notifications and post notifications with NSNotificationCenter to handle and notify of certain events.
They are using NSNotificationCenter to have the viewcontroller subscribe to UIKeyboardWillHideNotification and UIKeyboardWillShowNotification events.
Take a look at this one:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
NSNotificationCenter is designed to be used as a singleton ( I believe this is the correct term, correct me if I'm wrong ) so we access the NSNotificationCenter for this app's process by calling the class method defaultCenter. it adds observer 'self' ( which in this case is an instance of the view controller) and basically instructs it to send the message keyboardWillShow to the observer when a Notification under the name of UIKeyboardWillShowNotification is fired.
What object fires the UIKeyboardWillShowNotification? Well it's not a UITextView, this notification name is actually defined in UIWindow.h so it probably came from there, which in turn probably was invoked from UIKeyboard which is not a public API as far as I know.
NSNotificationCenter is a class. In Objective-C, classes are declared in header files; this one is in NSNotification.h. (Try pressing Command-Shift-O and typing "NSNotificationCenter" to find this yourself.) When you want to use a class, you #import the header file that the class is declared in; this makes the compiler read that header file first and make all the classes (and other globals) available to your code to use.
This would be a huge pain, though, since a typical Cocoa app uses zillions of classes and other globals from Apple's libraries. So, instead, you just need to #import <Foundation/Foundation.h> which is a header file that just includes a bunch of other header files, including NSNotification.h. (The import statement for Foundation is probably in your own header file, or something else like #import <UIKit/UIKit.h> which also will include foundation and ultimately NSNotification.h.)
One final detail is that there's is probably a "prefix" header in your project which includes UIKit.h in all of your files automatically, so anything declared in there is always available to your code.
NSNotificationCenter is a class in Foundation.
NSNotificationCenter doesn't need to be declared and stored in a variable, its just a call, to explain it further, think of NSNotificationCenter as a tackboard where things get posted, you add a note to that backboard by creating the NSNotificationCenter, and you assingn observers to look at that board, and do something when a note is added.
The NSNotificationCenter object being used in this case is a Singleton. What you need to know is that sending the message defaultCenter to the NSNotificationCenter class object always returns the same NSNotificationCenter object.
Here's what the call to default center might look like
+ (NSNotificationCenter*)defaultCenter
{
static dispatch_once_t once;
static NSNotificationCenter* sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
/*
set up properties, etc
*/
});
return sharedInstance;
}
I used to have my UIView that worked fine with several delegates some of them below. Now I changed that UIView from IB to be a UIScrollView (Now used as main view).
Since I've changed to UIScrollView my event delegates such as those below dont work anymore. Such as keyboard and also I had an element that I could move around and not it is just static.
I assigned all delegates form the IB that I could think of and did most things that I know. but i am running out of ideas on why the events are not getting triggered....
if i go back to the old UIView by doing cmd + z they work.
Could anyone point me in the right direction??
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
[self.view endEditing:TRUE];
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
// register for keyboard notifications
return YES;
}
EDIT - Complementary answer:
#Wezly Answer is totally valid.
But if anyone doesn't want to subclass the UIScrollView and use only UITextFieldDelegate methods.
another way of doing it is adding to viewDidLoad:
Note: you still cant access many things, but its another workaround
///
/// DelegateNotifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:self.view.window];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldDidEndEditing:)
name:UITextFieldTextDidEndEditingNotification
object:self.view.window];
I suggest that you subclass your UIScrollView object and add your touch events inside of it like below..
canvasObject.h
#import <UIKit/UIKit.h>
#interface canvasObject : UIScrollView
#end
canvasObject.m
#import "canvasObject.h"
#implementation canvasObject
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.canCancelContentTouches = false;
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
//Do Stuff
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
return YES;
}
#end
Then link your UIScrollView using the identity inspector in interface builder to the new scroll view subclass like below..
I was reading Apple Documents and saw that:
Before an object that is observing notifications is deallocated, it
must tell the notification center to stop sending it notifications.
Otherwise, the next notification gets sent to a nonexistent object and
the program crashes.
I tried to crash app to learn better how it is actually working.
However, even if I did not put this code inside SecondViewController dealloc, it still does not crash after sending notification. I'am obviously adding observer and going back from secondViewController and pushing notification in viewController. So, Why do we need remove observer if this program does not crash?
[[NSNotificationCenter defaultCenter] removeObserver:self];
Rest code is:
//ViewController:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. }
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated. }
- (IBAction)go:(id)sender {
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self presentViewController:secondViewController animated:NO completion:^{}];
[secondViewController release], secondViewController = nil; }
- (IBAction)push:(id)sender {
// All instances of TestClass will be notified
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification" object:self]; }
//SecondViewController:
#implementation SecondViewController
- (void)dealloc {
[super dealloc]; }
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
}
return self; }
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void) receiveTestNotification:(NSNotification *) notification {
// [notification name] should always be #"TestNotification"
// unless you use this method for observation of other notifications
// as well.
NSLog (#"Successfully received the test notification!"); }
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated. }
- (IBAction)back:(id)sender {
NSLog(#"");
[self dismissViewControllerAnimated:NO completion:^{}]; }
#Reno Jones is right.
Remove observer like this - [[NSNotificationCenter defaultCenter] removeObserver:self name:#"TestNotification" object:nil];
One more thing to add to his answer is you should remove the observer in the - (void)dealloc {} method - this is the method that is called when self is deallocated.
EDIT:
I've looked at the code and I have seen you are not using arc. one more question why are you not using ARC in your application? Do you have a good reason to stress yourself with reference counting , i do not see the point?
Second could you move the addObserver in the viewDidLoad method and see it that crashes your app.
Either dealloc method of SecondViewController is not being called because the object is still in the memory, or push:(id)sender method is not called after the controller is deallocated. Otherwise, It would definitely crash. Make sure that the methods are called by putting breakpoints.
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).