Okay I know there's a lot of posts on this, but I'm still having trouble. Here's the pseudo code for what I'm trying to do:
if(device is running iOS 5 or up)
#interface RootViewController : UIViewController <UIPageViewControllerDelegate, UIGestureRecognizerDelegate>
#property (strong, nonatomic) UIPageViewController *pageViewController;
else
#interface RootViewController : UIViewController <LeavesViewDelegate, UIGestureRecognizerDelegate>
#property (strong, nonatomic) LeavesViewController *leavesViewController;
endif
Am I right in thinking I need to use pre-processor macro checks since it's in the header file? It's a book app that should use UIPageViewController if it's iOS 5 or up (and therefore has UIPageViewController), otherwise it falls back on Leaves (https://github.com/brow/leaves). I have all the code set up. Just need to know how to tell the compiler which to use. I don't think using any runtime checks would work since I only need the protocol methods for either UIPageViewController or Leaves compiled, not both. And I'd rather not use completely separate source files. I've tried using these checks:
#ifdef kCFCoreFoundationVersionNumber_xxx
#ifdef __IPHONE_xxx
#if __IPHONE_OS_VERSION_MAX_ALLOWED <__IPHONE_xxx
(with various xxx's)
What am I missing here?
EDIT:
I also noticed this in the default .pch:
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
which makes me wonder why that same test didn't work in my .h file?
As I mentioned in the comments, you can't do this at compile time.
But here's an idea for you: It seems that the method names of UIPageViewControllerDelegate and LeavesViewDelegate do not intersect, so you could add the following to your header file:
-(void) leavesView:(LeavesView*)leavesView willTurnToPageAtIndex:(NSUInteger)pageIndex;
-(void) leavesView:(LeavesView*)leavesView didTurnToPageAtIndex:(NSUInteger)pageIndex;
-(void) pageViewController:(UIPageViewController*)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray*)previousViewControllers transitionCompleted:(BOOL)completed;
-(UIPageViewControllerSpineLocation) pageViewController:(UIPageViewController*)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation;
and not explicitly adopt the delegate protocols in the header file (leave out the delegates inside the < >).
Whatever classes you're using for these two could be instantiated in your *.m file in a condition along the lines:
// check for existence of class to determine which controller to instantiate
if(NSClassFromString(#"UIPageViewController"))
{
// do something and set UIPageViewController delegate to "self"
}
else
{
// do something else and set LeavesViewController delegate to "self"
}
Lastly, to get this to compile, you will probably need to forward declare all LeavesViewController- and UIPageViewController-related classes where you use them, and possibly utilize weak linking for some frameworks.
I haven't yet used Apple's UIPageViewController classes and protocols, so I can't provide much more insight than this. Be sure to let us know if you get something hammered out :)
you can't do this, as preprocessor macros are processed at compile-time. How should the compiler know, which iOS you're targeting, since you're compiling on your Mac and not everybody on his iPhone?
You cannot easily switch code at runtime. There are possibilities, but I don't think it is meant like you want it to be.
You can check at runtime if methods are available from specific SDKs. This is much simpler and straightforward. However you cannot achieve your goal with that.
I suggest:
Create a superclass, where you do not have the specific delegate protocols included. There you write all your code, you want to share.
Then create 2 subclasses from upper superclass. In each of the classes put in your specific code.
AND THATS IT.
This is the way it should be.
Related
I am aware that +(void)load method gets called in ios even before the main function.
In my case it is only getting invoked with UIView and not with UITextView.
Are there any files that need to import ?
Does load method work only for certain set of class.
Please provide necessary input regarding conditions required in order to invoke +(void)load method in ios.
Getting invoked here :
#implementation UIView (SomeMethodSwizzling)
+(void)load{
}
#end
Not Getting inovked here .
#implementation UITextView (SomeMethodSwizzling)
+(void)load{
}
#end
Usual reason is that you're not actually linking the file. Most common cause is that you don't directly use anything declared in it (you sometimes need to pass -objc to the linker to fix that). Another cause is you just forgot to add the file to the target (I see that happen a lot).
I'm new to iOS development and a bit stucked with such problem.
In my iphone app I'm using this awesome dropdown view controller https://github.com/nmattisson/DropdownMenu via Cocoapods.
I'm extending DropdownMenuController in my own MyDropdownMenuController:
MyDropdownMenuController.h
#import "DropdownMenuController.h"
#interface MyDropdownMenuController : DropdownMenuController
#end
I would like to override this drawOpenLayer (https://github.com/nmattisson/DropdownMenu/blob/master/DropdownMenu/DropdownMenuController.m#L126) method inside my controller instance, but unfortunately compiler says it's not possible:
MyDropdownMenuController.m
- (void)drawOpenLayer {
// compiler says
// "No visible #interface for "DropdownMenuController" declares the selector "drawOpenLayer"
[super drawOpenLayer];
}
Is it possible to override this method without actually updating external interface etc.?
You can create a category that defines the method
#interface DropdownMenuController (MichaelHacksIt)
- (void)drawOpenLayer;
#end
(no need to define the #implementation for this, because it's already implemented.)
Then you may just call that method.
Disclaimer:
Btw, that's the way to go if you want to call undocumented methods and don't care about Apples approval. In this case, there is nothing wrong with it, because you're not hacking Apple code, and Apple doesn't care if you hack some CocoaPods program. However, you are depending on internals of a third-party package, so there may be problems when you update that package next time...
Background.
Please consider the following steps:
1) In Xcode create a new "Single View Application".
2) Create a category NSObject+Extension.h and .m files:
// .h
#interface NSObject (Extension)
- (void)someMethod;
#end
// .m
#implementation NSObject (Extension)
- (void)someMethod {
NSLog(#"someMethod was called");
}
#end
3) Ensure that NSObject+Extension.m file is included into a main target.
4) Add the following line to AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSString new] performSelector:#selector(someMethod)];
return YES;
}
5) Ensure that #import "NSObject+Extension.h line does not exists anywhere in the app!
6) Run Application.
The output is
2013-08-27 04:12:53.642 Experimental[32263:c07] someMethod was called
Questions
I wonder if there is no any #import of this category anywhere in the app, how is it even possible that NSString does still have NSObject+Extension available? This behavior makes me feeling very bad about every Objective-C category I declare because I want the categories I declare to be available only in the scopes they are declared within. For example, I want NSObject to be extended by Extension only in some class but not in the whole app because its globalspace becomes "polluted" otherwise.
Is there a way to avoid this behavior? I do want my categories to work only when I explicitly import them, not when I just have them linked to a target I use to run.
I wonder if there is no any #import of this category anywhere in the app, how is it even possible that NSString does still have NSObject+Extension available? This behavior makes me feeling very bad about every Objective-C category I declare because I want the categories I declare to be available only in the scopes they are declared within. For example, I want NSObject to be extended by Extension only in some class but not in the whole app because its globalspace becomes "polluted" otherwise.
There are no namespaces on Objective-C objects. If you declare that a class has a method (whether via a category or on the primary #interface) then every instance of that class will have that method.
The way that Objective-C deals with "private" methods is by choosing not to tell other people about the methods in question (which is accomplished by not #import-ing the file that declares those methods). This, coupled with -Wundeclared-selector (warn if you use a selector that the compiler doesn't know about) is about as good of a guard as you're going to get.
But regardless, if you compile the .m file into your final binary, the method will exist, even if no one else "knows" about it.
Are there way to avoid this behavior? I do want my categories to work only when I explicitly import them, not just when I have them linked to a target I use to run.
Yeah, use -Wundeclared-selector, and Xcode will warn you.
Including the header just makes it so the compiler knows about it. It compiles it regardless because xCode compiles every file included in a target. At runtime, the method will be there, so even if you didn't include it for compile time checking, the object will still respond to that category method.
After reading about properties and ivars in Objective C I'm still confused with MasterDetail template for iOS in XCode.
MasterViewController declares property for DetailViewController:
#class DetailViewController;
#interface MasterViewController : UITableViewController
#property (strong, nonatomic) DetailViewController *detailViewController;
#end
And ivar for array of objects:
#interface MasterViewController () {
NSMutableArray *_objects;
}
#end
Why is it that way? I just can't get why those two things are declared differently.
Thanks.
Declaring something as a "property" allows other objects to access and work with it. In The case above, adding "detailViewController" as a property to MasterViewController means other objects can access and work with the methods & properties DetailViewController exposes.
While the "_objects" variable is internal (or private) to the MasterViewController.
Apple's documentation is generally excellent. Apple's templates are... sometimes a little challenged. They are also sometimes slow to be updated as the language improves (or they are updated erratically). The objects array should really be a private property rather than an implementation-declared ivar. In any case, don't read too much into this.
Remember, the view controller shouldn't even be holding the data; it should be getting it from the model classes (which the template doesn't provide). Some of this is in order to keep the templates simpler to use (they're not actually example code; they're templates). Some of the weird code is due to limitations in the templating engine. (They didn't used to be able to prefix your classnames, even though they told you that you must prefix your classnames; it was very annoying.)
Unfortunately, seeing something in example code also doesn't necessarily mean it's a correct way to code. Much of Apple's example code would be completely inappropriate in production code (most of their examples lack proper model classes, or fail to handle errors correctly). But again, that's kind of the nature of example code. Focus on the coding guidelines. They're much more useful than learning from templates and examples.
approach 1:
#interface MyController : UIViewController {
UILabel *myText;
}
#property (nonatomic, strong) UILabel *myText;
approach 2:
#interface MyController : UIViewController
#property (nonatomic, strong) UILabel *myText;
approach 3:
#interface MyController : UIViewController {
UILabel *myText;
}
I have read some articles talking about this kind of stuff but I still do not really realize which approach I have to adopt.
I also found that someone said approach 1 is a old way so I would like to know the best practice for ios sdk 6 using ARC.
I know that declaring variables using property is a easy way for generating getter and setter and someone suggested using it. However, I would like to ask in case a variable is not for calling by another class, is it necessary for the variable using property? and set it as private variable inside the interface? Or is it better for a variable only declaring inside the interface? I would like to learn the best practice so please forgive me if this is a silly question.
Moreover, some developers write #synthesize in this way
#synthesize myText=_myText;
but some write this:
#synthesize myText;
I would also want to know the difference and which one is preferable?
Thank you very much!
The most modern way1:
whenever possible, declare properties
don't declare iVars separately 2
don't #synthesize 3
locate as few properties as possible in you .h file 4
locate as many properties as possible in a class extension in your .m file 5
1 As of Xcode 4.5.2. Most of this applies back to 4.4, some of it won't compile on 4.2 (the last version available under Snow Leopard). This is preprocessor stuff, so it is all compatible back at least to iOS5 (I haven't tested on iOS4 but that should also be OK).
2 There is no point in declaring an iVar as well as a property. I am sure there are a few obscure cases where you would want to declare iVars instead of properties but I can't think of any.
3 Xcode will create an iVar with the same name as the property, preceded by an _underscore. If you (rarely) need some other kind of behaviour, you can manually #synthesize property = someOtherName. #vikingosegundo links us to this article on dynamic ivars, which is a use case for #synthesize. #RobNapier comments that you do need to #synthesize iVar = _iVar (bizarrely) if you are creating your own getters (readonly) and setters (read/write) for a property, as in this case the preprocessor will not generate the iVar for you.
4 The general rule with your interface: keep it as empty as possible. You don't actually need to declare your methods now at all, if they are for private use. If you can get the code to work without an interface declaration, that's the way to go.
5 This is an #interface block in your .m file, placed above your #implementation:
#TestClass.m
#interface TestClass()
//private property declarations here
#end
#implementation TestClass
...
You may also want to use #synthesize if you like a nice table of contents of your #synthesized properties that you can refer to and comment for clarity and organization.
Also, an #synthesize allows you to set a breakpoint on the property and trap when its value is changed.
When the compiler does everything for you, you end up being distanced from what is really happening and ignorant to it. However, not having to type out everything yourself all the time is also nice.