I was doing some iOS 5 tutorials and in several tutorials, it says things like "Add at end of viewWillAppear" or "Modify viewWillDisappear" in MasterViewController.m but in my project, MasterViewController.m does not have any of those methods.
Since it doesn't say "create those methods", I'm guessing they are automatically created by something but I do not know what I need to do to make them appear in MasterViewController.m.
Please somebody help me?
These methods are typically inherited from a parent class (e.g., UIViewController). What you probably should do is add something like
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Your new stuff here
}
The method prototype was taken from Apple's documentation of UIViewController; depending on the class you're subclassing (the thing after the colons in the .h file, in the #interface line) you might need different method prototypes.
Related
I am relatively new to Objective-C.
I have come to a code on the web that has something like this on rootViewController.m (this is a navigationController based app).
#interface RootViewController (CManagerDelegate) <CManagerDelegate>
#end
#interface RootViewController (PViewDelegate) <PViewDelegate>
#end
two questions:
what are these lines doing in the beginning of rootViewController.m
what are these lines doing in code? Please explain the stuff in parenthesis and between <> in this particular case.
thanks.
In one sentence: The code you posted makes the RootViewController class privately conform to some delegate protocols.
Delegate protocols are used to let a class declare the fact that it understands the messages from another class's objects. For example, a view controller can declare that it understands a gesture recognizer's delegate messages.
The fact that the class internally uses the gesture recognizer is often an implementation detail not relevant to other clients of the class. It is best not to publish this fact in the public interface but put it into the implementation (.m file).
Categories (and class extensions) let you do exactly this: Make a class conform to a protocol without changing the main #interface.
A nice and elegant solution!
Read up on Categories:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
And Protocols:
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/objectivec/chapters/ocProtocols.html
In fact, read all of Apple's Objective-c documentation before going any further:
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/objectivec/Introduction/introObjectiveC.html
Good luck.
I'm getting the following error
Property 'navigationController' not found on object of type
I've inherited this project and not sure what's going on.
In my m file in didSelectRowAtIndexPath I have
[self.navigationController pushViewController:nextController animated:YES];
It wasn't a problem before as I was accessing app delegate navigation controllers, which were outlets. However, I've had to move away from this approach as it's causing me problems. I've converted the rest of my project. But in this circumstance, where the project isn't using a normal table view, the navigation controller doesn't look to be available. I have this issue in 7 other views. Hoping I could have fixed this, and keep this cleaner code?
I'm really puzzled by this, I think this is occuring as SetsStyleSectionController isn't a view controller but is NSObject.
However, even with this set to UIViewController, the code runs, but doesn't push either.
Changing SetsSectionController from NSObject to UIViewController isn't available.
I'm not sure how to proceed?
I'm in the process of moving away from pushing from app delegate.
Edit: Screenshot 2 discussed below
I see a couple of issues here. You have a misunderstanding about protocols and classes, and you also have an application that interface with a protocol that while well-intentioned is actually making your life much harder than it needs to be.
The first issue you're dealing with is some troubles grokking the difference between protocols and classes, and between adopting a protocol and inheriting from a class. Which is totally fine, this stuff isn't easy. Basically, a protocol is just an interface to an object, and a class provides both an interface and an implementation. In other words, a protocol is just a list of methods that can be called, and a class is both a list of methods and the code to execute those methods. To get a more complete explanation, perhaps you'll be better off going straight to the source - Apple's "The Objective-C Programming Language" will probably help you, so please read about classes and protocols there. I think having done that you'll see why you're not having success giving your id<SetSectionController> instance a navigationController property without explicitly defining one. But do let me know if you have specific questions about this afterwards.
The problem that's harder to fix is this SetSectionController protocol. It has several issues and describing them all is outside the scope of this answer. Here's the thing - the implementation basically requires objects that implement this protocol to know which navigation controller is associated with the table view. This has been up to now provided deus ex machina by coupling them to the application's delegate, and you are right to remove this coupling. But now you have to find another way to get the right data populated into the view controller to push it on the navigation stack.
I think you should move the push logic into the view controller, and for now have the section controller provide an interface that gives the view controller the information it needs. So say the section controller has an implementation like this pseudocode:
- (void)...didSelectRow...
{
id detailsForIndexPath = self.dataForRows[indexPath.row];
DetailViewController *vc = [DetailViewController new];
vc.details = detailsForIndexPath;
[APPDELEGATE.navigationController push:vc];
}
Then I'd add a method to SetSectionController called something like -dataForRow: , the implementation of which would be like the first line of the method above. Then, in your view controller, implement ...didSelectRow... like this:
- (void)...didSelectRow...
{
id<SetSectionController> sc = self.sectionControllers[indexPath.section];
id details = [sc dataForRow:indexPath.row];
DetailViewController *vc = [DetailViewController new];
vc.details = details;
[self.navigationController push:vc];
}
If your section controller is doing anything else useful in ...didSelectRow... make sure to either move it to the view controller or forward ...didSelectRow... on to the section controller for now.
While I do appreciate trying to make complex table sections easier to manage through polymorphism, this protocol wasn't the right way to do it. It blindly copies methods out of UITableViewDelegate and UITableViewDataSource without consideration of whether those are the right questions to be asking something responsible for a single section of a single table. If you still want to use it, I think it will take some significant refactoring to get it into a shape that actually makes your life easier rather than harder. Depending on the complexity of the per-section logic deviation, I might just scrap it altogether. But that's a whole other question really. Hope this helps!
What do you mean it "isn't available"? Do you mean you don't want to/aren't allowed to subclass UIViewController, or are you getting a compiler error? From your comment on your question:
SetsSectionController.h:12:34: Cannot find protocol declaration for 'UIViewController'
you are changing the wrong thing to alter your subclass. As an example:
#import <Foundation/Foundation.h>
#protocol foo <NSObject>
- (void) bar;
#end
#interface lolcats : NSObject <foo>
#end
To change your superclass you change the class name after the colon. Ex.
#interface lolcats : NSObject <foo>
becomes
#interface lolcats : UIViewController <foo>
and you're off and running. However, if you accidentally change the protocol requirement for the protocol
#protocol foo <NSObject>
to
#protocol foo <UIViewController>
instead, or change the protocol you adhere to to <UIViewController>, you'll end up getting the EXACT error you got. You might be confused because the protocol says the object adhering to it must also adhere to the NSObject protocol, and NSObject is also a type of object. The object and protocol are separate things, and all objects inherit from NSObject, and thus inherit the protocol. Basically, it's saying "objects using this protocol must be objects."
Why did you expect this to work? The object is just a standard NSObject that states it adheres to a protocol containing a few methods. Why would it have a navigation controller property? It hasn't inherited it from anything. Regardless, based on your error, you probably just changed the wrong thing. Make sure you change the superclass class name after the colon.
Up until updating Xcode last night, this was working great (though probably through sheer dumb luck!).
I'm getting an error now for my UITextViews, Fields and UIPickerViews when assigning the delegate:
[m_textField setDelegate:GAMESTATE->glView.self];
m_textField is defined as UITextField*. glView is defined as a UIView*. To keep things simple, I'm using just one UIView for the entire app (could also be a problem). I only need access to a couple of basic operations in the app to get simple text info from the user. Now I get the error:
Cannot initialize a parameter of type 'id<UITextFieldDelegate>' with an rvalue of type 'UIView*'
Like I said, I probably was doing something wrong in the first place, and it only worked through sheer, dumb luck! If it helps, I'm already subclassing my UIView as an Accelerometer Delegate, as such:
#interface GLView : UIView <UIAccelerometerDelegate>
Any help, suggestions or tips would be greatly appreciated. I've stumbled through some books this morning and brushed up on several Apple docs, but a quick work-around doesn't seem possible. I'm not adverse to refactoring the way I take input from the user, I could just use a hand getting there.
You should create, for example, NSObject that will delegate
for UITextView : UITextViewDelegate.
for UIPickerView : UIPickerViewDelegate
Your class declaration should look like this:
#interface MyViewDelegator:NSObject<UITextViewDelegate, UIPickerViewDelegate>
Then you should alloc+init it and set it as delegator of your text/picker views:
MyViewDelegator *delegator = [[MyViewDelegator alloc] init];
[m_textField setDelegate:delegator];
Don't forget to implement appropriate methods in MyViewDelegator that are not optional for protocols.
I want to make a program that parses an XML file and then updates labels on 2 different tab bar views. On both of these views, I have a refresh button to update the results. What I'd like to do is update both of the views labels from either view. I figure the AppDelegate is probably a good choice to make this happen, but I tried creating classes that the AppDelegate can access but since they're instances of a class they contain no values. I get no errors but the labels don't update even though the data changes. This is the method in my AppDelegate that is called after the XML is parsed:
-(void)callUpdateValues {
NSLog(#"Calling Update from AppDelegate");
home *homeController;
data *dataController;
[homeController updateValues];
[dataController updateValues];
}
One of the update methods looks like:
- (void)updateValues {
NSLog(#"Call Home");
[label1 setText: [[[[totalData objectAtIndex:0] objectForKey:#"nodeChildArray"] objectAtIndex:7] valueForKey:#"nodeContent"]];
[label2 setText:[[[[totalData objectAtIndex:0] objectForKey:#"nodeChildArray"] objectAtIndex:1] valueForKey:#"nodeContent"]];
}
So the view calls the AppDelegate method "callUpdateValues" and then that method should call the individual "updateValues" methods in each view. I am not an expert on this by any means, and I'm really just trying to get an idea of how programming on the iPhone works. I'm probably just not understanding something here, and if someone could give me some sort of answer I'd appreciate it.
Cocoa has a number of classes available for notifying interested parties of changes. Directly calling methods as you describe makes things much more closely coupled than you need to.
In your method that generates the update you'd have:
[[NSNotificationCenter defaultCenter] postNotificationName:#"IGotSomeNewData"
object:newData
userInfo:nil];
And in the classes that want to hear about updates you'd register for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(newStuff:)
name:#"IGotSomeNewData" object:nil];
And then implement the method that gets called when something happens:
- (void) newStuff: (NSNotification *)notification {
id newData = [notification object];
// Do stuff
}
There's some really great stuff getting done by Apple for XML on the iPhone: XML Reading Material
The first snippet is out of place. I think what you're missing is that you need to create your instances within the AppDelegate.h, expose them using properties (and synthesizing them in the .m). Then you're update structure should fit better.
If you're just picking up iPhone programming, start digging into the guides that apple provides, and even if you're not into that, start pulling down at least 5 sample code projects a day. The beauty of them is that you can build them (even onto your iphone) and if you like a feature, you can see how it's done. Alternatively, get the grapefruit book from APRESS. Beginning iPhone.
Hope this helped.
In the example you gave, homeController and dataController are not properly initialized. If I understand your project correctly, you would have created instances of the homeController and dataController classes in your main XIB file, and connected them up to the appropriate views (label1 and label2). Your AppDelegate should, then, look something like this:
...
#class homeController;
#class dataController;
#interface AppDelegate
{
IBOutlet homeController * home;
IBOutlet dataController * data;
}
...
#end
With this in place, you would add (in your application XIB file), links from your homeController and dataController instances to the appropriate outlets (labeled home and data) in your application delegate.
Then, you could simply reference them by name in your callUpdateValues method:
-(void)callUpdateValues {
NSLog(#"Calling Update from AppDelegate");
[home updateValues];
[data updateValues];
}
On a side note, Cocoa coding standards usually specify that class names are capitalized. This is, of course, up to your personal taste, but if you're just getting started in Cocoa, it may be worth drinking one more cup of kool-aid at this point, just so your code will "fit in" with what most other developers are doing. Again, totally up to you!
In Apple's scrollView example they don't call that. I always thought that's a must. Why should I call that anyways?
If you are overriding the method you should still call the method in the super. Even if the super class is not doing anything with it today, Apple might one day change the implementation and your code will mysteriously stop working. If you really don't need to do anything in that method, leave it out of your code entirely, and the super's method will run as usual, without any intervention on your part.
No, you don't need to call [super viewDidLoad]. Edit: But read below, because I think you definitely should.
Let's be real here: Apple is not going to break thousands of apps, including those based on their published sample code, by deciding an event they're not currently handling suddenly needs to do something that developers may or may not want to stop and it's critical that if you don't need different behavior you not stop the event.
Edit: Having watched how Apple handles compatibility for an extra year, I now recommend learning and using the correct pattern. While I doubt your application binary will ever suddenly stop working, it's clear that the iPhone detects which SDK your binary was built against and modifies some OS behaviour based on this.
Apple might one day require a particular pattern be followed on some future SDK. This would not affect you until you rebuild with the latest Xcode + SDK, but then you'd get these breaks without any source code changes. Learn and follow the pattern to be safe.
As Markus says, UIViewController doesn't do anything in its viewDidLoad method, so you don't have to call it. However, it's a good habit to get into, in case you change your inheritance structure and suddenly the class that used to inherit from UIViewController now inherits from something that does do something in the viewDidLoad method.
Lets say you have 2 class, a Parent and a Child. Child inherits from Parent. They have a method called greet which returns a string.
Here is what the parent method looks like:
Code:
-(NSString *)greet {
return #"Hello";
}
We want the child to learn from his parents. So we use super to say greet how Mommy would greet, but with our own little additions too.
Code:
// Inherits from Parent
-(NSString *)greet {
NSString *parentGreeting = [super greet];
return [parentGreeting stringByAppendingString:#", Mommy"]
}
So now Parent greets "Hello", and the Child greets "Hello, Mommy". Later on, if we change the parent's greet to return just "Hi", then both classes will be affected and you will have "Hi" and "Hi, Mommy".
super is used to call a method as defined by a superclass. It is used to access methods that have been overriden by subclasses so that the class can wrap its own code around a method that it's parent class implements. It's very handy if you are doing any sort of inheritance at all.
Apple's documentation for viewDidLoad does NOT state that you should call [super viewDidLoad], so I would go with what Apple's says. Note, however, that for other similar methods like viewDidAppear, you must call [super viewDidAppear].
You don't have to call the [super viewDidLoad]
As far as I know, the viewDidLoad in the superclass (UIViewController) is only an empty function that gets called when the ViewController gets initialized with a nib-file.
So if you need to do any initializing, you should override this function and put your code there.
Just noticed that the static analyzer of Xcode 6 issues a warning if you do not call super in these functions. So it seems Apple now definitely wants us to call it.
Although in xCode 7 Beta/Swift 2 super.viewDidLoad won't compile. The error says it's only available in osx 10.10 and the auto-fix does this
if #available(OSX 10.10, *){
super.viewDidLoad()}
else
{
// Fallback on earlier versions
}
// My code
}