getting a warning setting up delegate for a custom protocol - ios

i added a custom protocol to one of my classes and i am getting a compiler warning when i attempt to set the delegate during a prepareForSegue: method. the warning i get is...
Sending 'MyCustomViewControllerClass *const __strong' to parameter of incompatible type 'id<NSFileManagerDelegate>'
the project builds and runs and everything works fine minus the warning. if i add <NSFileManagerDelegate> to my custom class the warning goes away. am i missing something or is this a bug in Xcode (6 beta)? the code is standard code for setting up a protocol / delegate but i will post it anyways...
SomeSecondClass.h
#import <UIKit/UIKit>
#class SomeSecondCustomViewController;
#protocol SomeSecondCustomViewControllerDelegate <NSObject>
- (void)doThisForMe
#end
#interface SomeSecondCustomViewController : UIViewController
#property (weak, nonatomic) id <SomeSecondCustomViewControllerDelegate> delegate;
#end
SomeSecondClass.m
#interface SomeSecondViewController ()
…stuff
-(void)someMethod {
[self.delegate doThisForMe];
}
#end
CustomClass.h
#import <UIKit/UIKit.h>
#import “ SomeSecondViewController.h”
#interface MyCustomViewController : UIViewController <SomeSecondCustomViewControllerDelegate>
//adding on <NSFileManagerDelegate> removes the warning...
#end
CustomClass.h
...standard stuff...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"MySegue"]) {
//this is where the warning happens on "self"
[segue.destinationViewController setDelegate:self];
}
}
- (void)doThisForMe {
//doing some stuff...
}
#end
i have opened previous projects where the warning did not exist and now the same warning appears. i am wondering if this is an Xcode problem?

You are running into an issue caused by an ambiguity in how Objective-C finds a matching selector and dealing with an id reference.
UIStoryboardSegue destinationViewController returns an id. Your code then tries to call the setDelegate method on this id reference. Since there is no information about what this id actually references, it doesn't know which setDelegate: method you might mean (there are many). So the compiler scans through the list it knows of and picks one. In this case it chose the setDelegate: method from the NSFileManager class. Since self doesn't conform to the NSFileManagerDelegate protocol, you get the warning.
You could ignore the warning and your code will work fine in this case.
The better solution is to help the compiler by adding a cast:
[(SomeSecondCustomViewController *)segue.destinationViewController setDelegate:self];
This will let the compiler know which setDelegate: method you really mean.
BTW - Adding NSFileManagerDelegate to your class is not a valid solution even if it works at the moment. A simple reordering of some import statements could lead the compiler to make a different choice and your warning would return but complain about not conforming to some other protocol.

as it turns out, this is a bug / change in Xcode 6 beta. running this exact same code on Xcode 5.1.1 produces no warnings or errors. the problem is cause because in Xcode 6 the compiler is asking for type
(id<NSFileManager>)
for the delegate. in Xcode 5.1 the compiler is simply expecting
(id)
for the delegate type.
as rmaddy stated, by casting the type in
[(SomeSecondCustomViewController *)segue.destinationViewController setDelegate:self];
it did remove the warning, but this should be an unnecessary step and will chalk it up to a problem with Xcode.

Try explicitly typing (giving type to) the destinationVC, like this:
SomeSecondCustomViewController *vc = (SomeSecondCustomViewController *)segue.destinationViewController;
vc.delegate = self;

#rmaddy provides the correct answer. This is a more detailed example for those of us who are lightly schooled in the ways of Objective-C.
I changed my code from:
[[segue destinationViewController] setDelegate:self];
UIPopoverController *popoverController = [(UIStoryboardPopoverSegue *)segue popoverController];
self.flipsidePopoverController = popoverController;
popoverController.delegate = self;
to:
[(UIPopoverController *)segue.destinationViewController setDelegate:self];
UIPopoverController *popoverController = [(UIStoryboardPopoverSegue *)segue popoverController];
self.flipsidePopoverController = popoverController;
popoverController.delegate = self;
and the warnings disappeared.

Related

Xcode CodeSense error - Property not found on object of type, but project compiles

I have this in my .pch file:
#import "UIViewController+Loader.h"
The implementation for the category looks like this:
static char kUIViewControllerBaseViewKey;
#implementation UIViewController (Loader)
- (void)setLoader:(LoaderView *)loaderView {
objc_setAssociatedObject(self, &kUIViewControllerBaseViewKey, loaderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (LoaderView *)loader {
LoaderView *loaderView = (LoaderView *)objc_getAssociatedObject(self, &kUIViewControllerBaseViewKey);
if (!loaderView) {
loaderView = [[LoaderView alloc] initWithView:self.view];
[self.view addSubview:loaderView];
[self setLoader:loaderView];
}
return loaderView;
}
#end
The problem is that in any view controller when I do this:
[self.loaderView doSomething];
Xcode shows this error: Property 'loaderView' not found on object of type MyViewController
HOWEVER, the project compiles fine and the doSomething: method on loaderView works fine. How can I get Xcode to stop showing these errors?
After much head scratching, I found a build setting called "Increase Sharing of Precompiled Headers". Changing this to YES made the problem go away. After reading the description of what this does, I still don't understand why this fixes the problem. But it's fixed.

iOS7 compiler won't recognize method call with message

So this one has me stumped - probably something simple, but I'm clueless.
I'm defining a custom class, containing one method that receives one message (an integer). When calling that method, the compiler refuses to recognize the message I'm trying to send along with the call. ("No known class method for selector 'sendMessage:'. Removing the message from both the call and the definition - i.e. removing the :(int)mode from the definition, and the :1 from the call - allows it to compile fine (but then of course I lose the functionality).
I've tried defining it as an instance method, and as a class method - neither one works.
Many thanks in advance for your collective wisdom!
custom class "Communications.h":
#interface Communications : NSString
+(NSString*)sendMessage:(int)mode;
#end
Communications.m:
#import "Communications.h"
#interface Communications ()
#end
#implementation Communications
+(NSString*)sendMessage:(int)mode {
// Do something important
}
ViewController.h:
#import "Communications.h"
- (void) tapPanic:(UITapGestureRecognizer*)sender;
ViewController.m:
- (void) tapPanic:(UITapGestureRecognizer *)sender {
[Animations animatePanic:self.view type:0];
panicactive = 1;
NSString* tmpResponse = [Communications sendMessage:1];
UILabel* tmpServerResponsePanic = [self.view viewWithTag:10002];
tmpServerResponsePanic.text = tmpResponse;
[[self serverResponsePanic] setNeedsDisplay];
}
So, chalk it up to weirdness with Xcode... copy / pasting the contents of Communications .h and .m into new files, with a new class definition (Comms), did the trick. I think the compiler got confused and was remembering an old definition of the method.

App suddenly crashes while processing presentViewController method in iOS 7.1

Today, I upgrade my Xcode to the latest version(Version 5.1 (5B130a)) to support iOS 7.1.
After doing this, I run my project in Xcode as usually. Then app crashes.
I didn't change any code before upgrading the SDK version.
The code is running perfectly in iOS 5.x, 6.x, 7.0.x.
I am simply presenting another view controller in the current view controller.
they are both initialized by storyboard.
While processing presentViewController method, it gets a error message "Thread 1: EXC_BAD_ACCESS (code=2, address=0x60)". I have checked the variables, they are both alive, not a released garbage.
What's the problem with iOS 7.1??
the project is using non-ARC mechanism.
Here is my code:
#property (nonatomic, retain) ArticleViewController *articleView;
....
self.articleView = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"ArticleViewController"];
...
[self presentViewController:self.articleView animated:NO completion:^() {
log(#"has shown article page...");
}];
but it works fine if presenting another view by using addSubView function:
[self.view addSubView:self.articleView.view];
I really don't know why this happens.
This happened to my app while presenting a view controller with modalPresentationStyle = UIModalPresentationCustom;
Here's how my code looks like on iOS 7.0:
//Inside my MyPresentedViewController:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[self setupContent];
}
return self;
}
- (void)setupContent
{
//TransitionManager adopts UIViewControllerTransitioningDelegate and UIViewControllerAnimatedTransitioning
TransitionManager *transitionManager = [[TransitionManager alloc] init];
transitionManager.presenting = YES;
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = transitionManager;
}
However, the above code crashes on iOS 7.1 so I changed the implementation to:
#interface MyPresentedViewController
#property (nonatomic, strong) TransitionManager *transitionManager;
#end
...
//Inside my MyPresentedViewController:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[self setupContent];
}
return self;
}
- (void)setupContent
{
//TransitionManager adopts UIViewControllerTransitioningDelegate and UIViewControllerAnimatedTransitioning
self.transitionManager = [[TransitionManager alloc] init];
_transitionManager.presenting = YES;
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = _transitionManager;
}
Basically, instead of declaring a transitionManager object inside the setupContent method, I created a private property (strong reference) for it.
Hm the only thing I can think of is ensuring that your articleView property is a strong reference.
#property (nonatomic, strong) ArticleViewController *articleView;
I ran into a similar issue with a picker view. In my case the pickerview was set to nil after the selection handler block, although this caused no issues as recently as last night, it was causing the app to crash this morning. Removing that line fixed the issue, however I have converted this project to ARC in the last month so you may have to find a better way to handle clean up.
This may be completely unrelated to the stated problem or answers but I ran into a similar problem when accessing an NSString on an iPhone 5S. The exact same code runs fine on an iPad and iPad at the same time.
To start out with I think it's important to state that I'm running under ARC, and I have been told repeated not to "release" any objects I instantiate in my functions. I've had problems with this before so I use some of my C# background to set almost all of my local variables to nil (null in C#). Yes, I don't trust MS GC either.
Back to the problem at hand; I have an NSString called 'data'. 'data' is read as a result from another method in a different class. Using NSLog I can see the contents of 'data'. On the next line I convert 'data' to an array to use it in scanf. That still works. On the third line I try to NSLog 'data' again, but then I get the EXC_BAD_ACCESS error. Each time it has a different address. I'm even less comfortable with ARC than I am with the Microsoft Garbage Collector so I wrapped the function in a try..catch..finally. 'data' is now sitting outside of the try..catch..finally. I'm setting 'data' to nil in the finally, which seems to fix the problem.
I know this was a bit long winded, but I would really appreciate it someone could explain why this would happen. I'm expecting to see a lot of these problems to popup all over my code now.

Is it safe to import a view controller to another controller for segueing?

I am currently going through the iTunes U Stanford iOS dev. course and I am trying to utilize segues.
In my prepareForSegue method I am trying to update the data on the transitioning VC and this is my code:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"changeToScreen2"])
{
if([segue.destinationViewController isKindOfClass:[Screen2ViewController class]])
{
"Code to be implemented"
}
}
}
But my Screen2ViewController isn't recognized. Is it safe and proper coding technique to import a view controller to another view controller for segueing purposes or is there another method I should implement?
-------------------------------------------------------------------------------------
I have a new problem now
When I set the values of a UILabel and UITextView with the aforementioned prepareForSegue method and change to Screen2ViewController the labels and text views have not be updated with the values that I have added.
Screen2ViewController *S2VC = (Screen2ViewController *)segue.destinationViewController;
S2VC.myLabel.text = #"Screen 2 is now being viewed";
S2VC.uneditableText.text = #"Why aren't you showing up when I push you";
But these values don't get updated.
Yes it is safe to import view controllers. There are a few caveats however,
Do not import 2 headers into each other, this will cause non-obvious error.
Screen1ViewController.h
#import "Screen2ViewController.h"
Screen2ViewController.h
#import "Screen1ViewController.h"
Import in the .m file instead
Screen1ViewController.h
#import "Screen2ViewController.h"
Screen2ViewController.h
//No imports
Screen2ViewController.m
#import "Screen1ViewController.h"
As a general rule I try to put all the imports in the .m file: both for encapsulation and the above reason. You can also foreword declare a class if you need to use both classes in both header files.
About your new problem: you can only update instances from another view controller if they're made public (in other words, they're declared in its header file). So, with the provided code, you'd need to make myLabel and uneditableText public. However, during prepareForSegue: execution they were not yet allocated. As all you need from those objects is editing their text, it would be better to define two NSString's in the second view controller and then, inside that VC's implementation, you assign them to the objects. Example:
First View Controller
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"changeToScreen2"])
{
if([segue.destinationViewController isKindOfClass:[Screen2ViewController class]])
{
Screen2ViewController *S2VC = (Screen2ViewController *)segue.destinationViewController;
S2VC.labelText = #"Screen 2 is now being viewed";
S2VC.textViewText = #"Why aren't you showing up when I push you";
}
}
}
Second View Controller's Header
...
#property (nonatomic, strong) NSString *labelText;
#property (nonatomic, strong) NSString *textViewText;
...
Second View Controller's Implementation File
...
- (void)viewDidLoad
{
[super viewDidLoad];
self.myLabel.text = self.labelText;
self.uneditableText.text = self.textViewText;
}
...
Needless to say you must have previously used the Interface Builder to add myLabel and uneditableText as #property's of your Second View Controller.

In iOS prevent RootViewController to become God like by extra 'controller' to display MFMailComposeViewController

I've asked a question about the same issue before, and the solutions worked, but it was not a compatible iOS 4.3 solution, and I thought my design is not the right one.
Now I want to show a MFMailComposeView(Controller) as a modal view on top of my RootView(Controller) when i press a button. And instead of making it the delegate i want to make a simple NSObject which implements the protocol.
Who is also capable to show the MFMailComposeView(Controller) in the RootViewController.
I am trying this design/solution which gives me memory allocation/access problems.
RootViewController.m:
- (IBAction)tapExportButton:(id)sender
{
SendMailController *sendMailController = [[SendMailController alloc]initWithParentViewController:self];
[sendMailController openMailDialog];
[sendMailController release];
}
SendMailController.h
#interface SendMailController : NSObject <MFMailComposeViewControllerDelegate>
- (id)initWithParentViewController:(UIViewController *)parentViewController;
- (void)openMailDialog;
#property (retain, nonatomic) UIViewController* parentViewController;
#end
SendMailController.m
#import "SendMailViewController.h"
#implementation SendMailController
#synthesize parentViewController = _parentViewController;
- (id)initWithParentViewController:(UIViewController *)parentViewController
{
if (self=[super init]) {
self.parentViewController = parentViewController;
}
return self;
}
- (void) dealloc
{
self.parentViewController = nil;
[super dealloc];
}
- (void)openMailDialog
{
if ([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
...
mailer.modalPresentationStyle = UIModalPresentationPageSheet;
[self.parentViewController presentModalViewController:mailer animated:YES];
[mailer release];
}
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
switch (result)
...
// Remove the mail view
[controller.parentViewController dismissModalViewControllerAnimated:YES];
}
#end
When I set a breakpoint in the delegation method, it crashes already before that.
Is is something with the delegate property of mailer (MFMailComposeViewController)?
The problem is that you create your instance of SendMailController and try to show the composer view, and then you release the SendMailController. This causes it to be deallocated. It looks like it works because the composer view is on screen - this is because it has been retained by the presentModalViewController call.
To fix, you need to retain your instance of SendMailController and release it when the composer has been dismissed.
The correct way to do it (and required if you use ARC, and you should use ARC) is to provide a delegate callback to tell the owner that it's finished - which kind of makes the class pointless if all it does is wrap the composer.
The cheating way (which only works when not using ARC, and which you need to be very careful with) is to have your object retain itself when it presents the composer and release itself when the composer is dismissed.
The underlying problem being your root view controller containing all the logic, you should look at using child view controllers (if a single screen holds all your UI). Usually though, your root view should be the simple class (like a master list of options) and the views it presents would be more complex (the detail views). You need to look at ensuring that the appropriate class owns the responsibility for each screen of UI.

Resources