NSObject ModalView and Delegate - iphone

I'm modifying someone else's code and trying to use a ViewController however it is currently an NSObject. So below I have added newDelegate
#interface myAppDelegate : NSObject <UITableviewDelegate, newDelegate>
in my code I try to bringup a modalview with
[self presentModalViewController:newModalView animated:YES];
I get the error message 'myAppDelegate' may not respond to '-presentModalViewController:animated:' that's fair enough, it is an NSObject. Can someone help me with a possible approach?

If someone wrote code with an NSObject as a UIViewController, you've got a lot of problems. "Modifying someone else's code" means to me "I'm the new vendor after the old guy screwed it up so bad by lying to the client and SAYING they could do iPhone dev work". Maybe go back to the client and say "we need to redevelop from scratch, this is garbage"? :)
As for the issue at hand - app delegates ARE NSObjects -- so that part looks right. However, an app delegate should not usually be a UITableViewDelegate. You'd have a window property and a rootViewController property in the app delegate, and the final call of appDidFinishLaunchingWithOptions: would instantiate the view controller and load it into the window. That view controller would then handle any views (and likely be the UITableViewDelegate in this case).
The presentModalViewController: code is built-in to UIViewController, so you're not going to be able to it to work directly from the app delegate.

Related

Separating delegates from ViewController to own classes

I'm pretty new to iOs development and I've become a bit confused how should I separate my code to avoid a huge ViewController.m-file. Currently my main viewcontroller has quite a many delegates:
#interface ViewController : UIViewController <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
I would like to reduce the viewController code and I guess I should create separate classes to handle delegate tasks. The problem is that if I for example create singleton class for CLLocationManager and let it implement delegate methods, how do I then communicate with the view controller?
Let's say that my CLLocationManager receives a location update, how do I tell the viewController to make changes to the UI? Should I use NSNotificationCenter to post a notification and then add observer to the view controller? Or is the best way just to let viewController implement all delegate methods as it is now?
Move some of that functionality into your data model.
It's hard to say how you should manage this given the limited information you've provided, but one has to wonder whether a single view controller should really be managing a map view and keeping track of location and managing a HTTP connection and managing a Core Data fetch. Why not move some of that into your model, where it'll be somewhat easier to divide into modules?
Let's say that my CLLocationManager receives a location update, how do
I tell the viewController to make changes to the UI? Should I use
NSNotificationCenter to post a notification and then add observer to
the view controller?
A notification would be a good solution -- it provides a way for the part of your program that manages location (again, this probably belongs in the model) to communicate the change without having to know anything in particular about the parts of the program that care about changes to location. It also allows one-to-many communication -- if you have another view controller that also cares about location, it can observe the same notification.
Or is the best way just to let viewController implement all delegate methods as it is now?
I try to think about dividing responsibilities appropriately more than limiting the size of the code. If you have a class that does one job but needs a lot of code to do it, that's fine. If you have one class that manages many unrelated things, that's not so good. The trouble is that a lot of jobs seem to fall into the traditional "manages a screenful of content" role of a view controller. Try to separate the task of managing the presentation of the data (which is the view controller's rightful job) from managing the data itself (which is the model's job).
Implement a class responsible for delegate methods:
#interface DelegateManager : NSObject <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
-(id)initWithViewController:(ViewController*)vc;
#property (weak) ViewController *delegate;
#end
In your ViewController:
#interface ViewController : UIViewController
-(void)doSomething;
#end
In your ViewController, create an instance of DelegateManager with self as parameter. Set all your delegates' target to your DelegateManager. In the delegate methods of your DelegateManager, call [self.delegate doSomething]; to communicate back to your ViewController.

Obj-C, Property 'navigationController' not found on object of type, from NSObject class?

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.

How do runtime-created sub views communicate with the view controller?

In my iPhone project I have a UIViewController in which I add an instance of a subclass of UIView. From this instance I need to communicate back an integer to my view controller. My current solution is to send a message from my instance to my App Delegate which sends a message to the view controller. It works, but it feels a bit messy.
Is there anyway I can send a message straight back to the view controller?
I would love to be able to do something like [super doSomething:123];
Any ideas?
Thanks
This is the kind of thing that NSNotificationCenter was provided for. Once you get handy with sending and receiving notifications, your message-passing gets a WHOLE lot simpler.
One of the classic things people confront is how to get a pointer to the object they want, in order to tell it about something. How do I, for instance, tell the ViewController two slots back up the UINavigationController stack that the user just changed this data field? So you dig into the stack, offset back by some magic number of elements in the stack, build public setters on the fields you want talk to... It's super cumbersome.
Compared to registering as a notification receiver in one place, and then firing a notification in some complete other place when the data changes. It's kind of magical, after doing all the "dig through the view hierarchy" work.
Um, I'm not sure I understand your problem correctly. You have a class derived from UIView which needs to send a message to another class derived from a UIViewController. It sounds like you are creating the UIView instance programmatically. Is there any reason my you could not have a property on the UIView which refers to the UIVIewController and just use that to send it a message directly.
You cannot use [super ...] because the super of your UIView derived class would be UIView.
Or am I miss-understanding the issue :-)
If I understand correctly, you want to send a message from your subclass of UIView to the view controller.
That means your subclass of UIView needs to have a property or ivar which is the view controller. The easiest way to do this is to add it as an outlet and connect it to the view controller in the nib file.
Generally you should not go via the app delegate. Having a typed pointer link is also less than ideal.
The optimal way of communicating - Apple does it like this as well - is to create a delegate protocol. When creating the view controller you pass a pointer to the delegate as id . Then when it gets to sending the message you ask the delegate:
if ([delegate respondsToSelector(didFinishSomething:)])
{
[delegate didFinishSomething:info_to_pass];
}
If you want to be extra-sophisticated then you can also add a pointer to the calling class instance. Like:
[delegate myViewController:self didFinishSomething:info_to_pass];
This way you always know what kind of class the message is coming from.
If there is more than one place that needs to be notified of a change, then instead of delegation you will use notifications.
In my iPhone project I have a
UIViewController in which I add an
instance of a subclass of UIView.
This implies that you have both a reference to the instance of the UIView subclass and the UIViewController in the same scope. I.e. something equivalent to:
UIViewControllerSubclass *myViewController;
UIViewSubclass *myView;
(It doesn't matter if they are actually instance variables or, even, globals)
And once those two variables are initialized, somewhere you do something like:
myViewController.view = myView;
In your UIViewSubclass, add a property that points back to your UIViewControllerSubclass:
#property(assign) UIViewControllerSubclass *myController;
Then, when you do the above assignment, add:
myView.myController = myViewController;
From there, messaging your controller from your view is easy:
[self.myController yoManHereIsAnInt: 42];
Note that I used assign instead of retain because the controller already retains the view. If the view were to also retain the controller, you would have a cycle that would eventually lead to a leak.
No super about it. super is entirely related to the inheritance hierarchy of your Objective-C classes. What you are asking has nothing to do with inheritance and everything to do with how the various instances of objects in your application are connected together.
Simply add an outlet to your UIView subclass, connect it to its view controller in Interface Builder, and call your method on that. Here’s how that might look:
MyUIView.h:
#interface MyUIView : UIView
{
UIViewController *viewController;
}
#property (assign) IBOutlet UIViewController *viewController;
#end
MyUIView.m:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self viewController] setTitle:#"Hello from MyUIView"];
}

Passing info to a delegate from a UIActionSheet

My understanding about passing data back to a delegate completely revolves around making a new view controller and having it conform to a protocol.
I am trying to get a time input back from a UIDatePicker set with the UIDatePickerModeCountDownTimer mode and I am running into issues.
Inside of Main1.m I create a UIActionSheet *action, and set everything up so that it presents itself with a UIDatePicker on a click. Inside of Main.m I also say:
main.delegate = self;
If this were not a UIActionSheet, I would make a protocol reference inside the new ViewController and then have the new vc pass data to a method that Main has, but I can't do that with a UIActionSheet!!
What am I missing? I assume there is something inherently different about Action Sheets, ut I can't figure it out.
Thanks for your help!!!
EDIT:
Sorry! I was giving a generic name to my view controller. It isn't actually Main.m, its FirstViewController.h/m
I didn't realize that my generic reference was getting mixed up with the Main.m file that is completely different than a vc.
I don't exactly understand why you're putting your delegate assignment in Main.m. I assume that you're setting up your UIActionSheet in a ViewController, and launching it from there. In this case, your ViewController is your delegate object. So you need to make sure that your ViewController implements the UIActionSheetDelegate. ie:
#interface SomeController : UIViewController <UIActionSheetDelegate>
Then you simply implement the required methods of that delegate in your view controller class, and that should do it. If I'm missing something about how you're implementing this, then you need to provide more code examples to examine.

Possible to use UIActionSheet from Application Delegate?

I have a common UIActionSheet which I use across about 10 different view/view controllers. I'm wondering if it's possible to use UIActionSheet from the app delegate in order to prevent code duplication?
So far my attempts to use an action sheet from the delegate haven't worked, I suspect my problem lies when calling the showInView method - do I need to instantiate an object of my view controller then use viewController.view here? If so how can I then tell which view called the action sheet method from the delegate?
I didn't try the approach proposed by c_phlat, but I wonder to what self.view is mapped.
I did manage to do it like this:
[actionSheet showInView:window];
it works just as well.
I was having the same problem, and I recently figured out a way to fix it in my app. The key for me was to make my app delegate class an extension of UIViewController rather than NSObject. (I think UIViewController is a subclass of NSObject anyway, so this shouldn't affect your app too much.)
In other words, change the main implementation line in your app delegate interface file from something like this:
#interface YourAppDelegate : NSObject <UIApplicationDelegate, UIActionSheetDelegate> {
To this:
#interface YourAppDelegate : UIViewController <UIApplicationDelegate, UIActionSheetDelegate> {
You should now be able to use the showInView: method with your action sheet within your app delegate implementation:
[yourActionSheet showInView:self.view];