iOS Dev via Xcode: UIView & UITextView/UIPickerView - Cannot Assign Delegate? - iphone

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.

Related

Declaring delegates on .m

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.

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.

UIButton simple subclass (need to have an NSString property like a tag)

I have a view that has a bunch of programmatically-created UIButton objects (rounded rectangle type), but I need to store a little bit of information in them, identifying each button. I was doing fine with the tag, but the numbers got too large, and I would like to basically have another piece of info associated with each button, this time an NSString. By the way, the label is taken, I can't use that. What's the simplest/quickest way to go about this? Do I need to subclass UIButton and add my own property to it like stringTag? I'm not super proficient in obj-c yet, and read somewhere this is a pain. Is there a simpler way to store an additional string in my UIButtons? Thanks a lot in advance for any advice.
Subclassing uibutton is a real pain. They don't usually work as expected since UIButton is an abstract class. Therefore to make a fully working button you may have to override quite a few methods to get the button to do everything the the Apple subclasses do. Instead if you just need to add storage you can use associative references. you must #import <objc/runtime.h> to use the functions.
The best way to do this is to define a string constant NSString *const buttonTagName = #"com.youapp.buttonTag"; and store tags with this as the key objc_setAssociatedObject(button, buttonTagName, tagForCurrentButton, OBJC_ASSOCIATION_RETAIN); .
I think adding an NSString with some id info shouldn't mess up your UIButton subclass too much. At least it's easy to try it out, we are talking 10 lines of code here. I subclassed a UIButton and overrided initWithFrame method once and got it working correctly. But it's true, it is a pain and don't ask me why I did it :)

NSObject ModalView and Delegate

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.

Do I have to specify a protocol if I want my class to have a delegate?

I'm new to Objective-C and development on Apple's platforms, but hopefully I can frame this question in an understandable way regardless :)
I want to parse an XML feed for my iPhone app, and I decided instead of shoving all of the delegation methods from an instance of NSXMLParser into my view controller, I'd wrap this up inside of a FeedParser class. After reading a few docs and example code, here's what I came up with:
#protocol FeedParserDelegate <NSObject>
- (void)parserDidLoadEpisodes:(NSArray *)episodes;
#end
#interface FeedParser : NSObject {
id <FeedParserDelegate> delegate;
}
#property (nonatomic, assign) id <FeedParserDelegate> delegate;
- (id)initWithURL:(NSURL *)url delegate:(id<FeedParserDelegate>)theDelegate;
#end
Simple. Delegates of my FeedParser object just have to implement parserDidLoadEpisodes.
However, as I started actually making my FeedParser class use NSXMLParser, I realized that I didn't have to specify that FeedParser implements a protocol for NSXMLParser -- I could just implement the delegate methods that I wanted to do something with. I think I've noticed this with other classes that follow the delegate pattern, too, but not all.
So why wouldn't I too just not bother with specifying a formal protocol for my FeedParser class? It would cut away some perhaps unnecessary code. I guess the question is: why would I want to create a formal protocol instead of just doing something like just checking to see if the method is implemented on the delegate with respondsToSelector? Is it only so that the compiler will issue nice warnings if a required delegate method isn't implemented?
A protocol declared with #protocol is called a "formal protocol". The other way to do it is to declare a category (a set of additional methods) on NSObject, like so:
#interface NSObject (FeedParserDelegate)
- (void)parserDidLoadEpisodes:(NSArray *)episodes;
#end
Then simply define that method for any object that you want to be a feed parser delegate, and leave it undefined otherwise. This is called an "informal protocol".
Why are there two ways? Well, here's a hint: the informal protocols came first. What it comes down to is that they added formal protocols because informal ones weren't cutting it. Informal protocols make it too easy to forget an important method or try to use an object as a delegate for something it's not designed to work with.
Basically, for the cost of adding <FeedParserDelegate> here and there, you can get the compiler to do your debugging for you. The compiler will generate warnings for the most common delegate bugs, which saves you time if you make one of those mistakes. Why not take advantage of its helpfulness?
Always, always, always favor compile-time error checking over run-time. You could of course ask the class if it supports the method, but why ask when you can know?
The answer is no, you don't have to. But you should want to. :)
Adding a protocol makes the compiler check things for you. If you don't get those nice errors at compile time, then you'll get them later at run time when they're harder to track down.