I have category for supporting portrait only when vc is presented over another vc. To use that, normally we use import UINavigationController+Portrait.h. However, when I try to call in another vc, although I haven't import yet, supportedInterfaceOrientations is always calling in my category. May I know what is wrong?
#import "UINavigationController+Portrait.h"
#implementation UINavigationController (Portrait)
- (BOOL)shouldAutorotate
{
return NO;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
//ios4 and ios5
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return NO;
}
When you create a category in Objective-C, any methods you define will affect every instance of the class the category is created for – it doesn't only take effect in files where you import the category.
The Objective-C runtime is dynamic, meaning that when you call a method, it will look up the method on the appropriate class and call it. When you override that method via a category, even without importing it anywhere, the runtime will look for the right method to call and find that the one defined in the category is available and instead call that.
That's the reason that all instances of UIViewController now find your category's method instead.
Because of this, overriding methods in categories is pretty dangerous. Instead, the right thing to do is to subclass a class and override its methods there. If that isn't an option, method swizzling is another way of doing it, but that also has its own risks.
Related
how can I apply haptic feedback throughout my app for every touchupinside event of every UIButton without writing code for each individual button? I have tried making a UIButton category and overriding - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event but this messes up some of my UIButton actions, (i may have implemented it badly)
does anyone have any suggestions?
Overriding in categories such kind of methods that mentioned #ChoungTran isn't such good idea. If you want to customize the default methods in a Category, it'd be better to do that in a swizzled method.
But, I'll prefer to make a custom class derived from UIButton, and implement that logic there and use that button everywhere where I need haptic.
From Apple documentation:
Although the Objective-C language currently allows you to use a
category to override methods the class inherits, or even methods
declared in the class interface, you are strongly discouraged from
doing so. A category is not a substitute for a subclass. There are
several significant shortcomings to using a category to override
methods:
When a category overrides an inherited method, the method in the
category can, as usual, invoke the inherited implementation via a
message to super. However, if a category overrides a method that
exists in the category's class, there is no way to invoke the original
implementation.
A category cannot reliably override methods declared in another
category of the same class.
This issue is of particular significance because many of the Cocoa
classes are implemented using categories. A framework-defined method
you try to override may itself have been implemented in a category,
and so which implementation takes precedence is not defined.
The very presence of some category methods may cause behavior changes
across all frameworks. For example, if you override the
windowWillClose: delegate method in a category on NSObject, all window
delegates in your program then respond using the category method; the
behavior of all your instances of NSWindow may change. Categories you
add on a framework class may cause mysterious changes in behavior and
lead to crashes.
Try to use category with class UIButton. In UIButton-Extention register event forControlEvents:UIControlEventTouchUpInside
#import "UIButton+Extention.h"
#implementation UIButton (Extention)
- (instancetype)init
{
self = [super init];
if (self)
{
[self addTarget:self action:#selector(customTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(void)awakeFromNib
{
[super awakeFromNib];
[self addTarget:self action:#selector(customTouchUpInside:)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)customTouchUpInside:(UIButton *)sender
{
//TODO:
NSLog(#"Do something here...");
}
#end
Updated: Register event when UIButton created programmatically
I am having a singleton class MyController of type UIViewController. I could able to access the view property like [MyController sharedInstance].view and could able to set that to nil like,
[MyController sharedInstance].view = nil;
I wanna restrict someone accessing view property. How could I stop/restrict that?
I found the solution myself. I overridden the method as
-(void)setView:(UIView*)view {
if (view == nil) {
//ignore - make no change
}
else {
//default performance
[super setView:view];
}
}
Class ProjectSingleton {
static let shared = ProjectSingleton()
//here you can make any variable or function and use it anywhere in project
}
I am using the controller in multiple screens. If the controller is created n number of times, it adds weight to the app. So, to overcome that, I am creating it once and reusing it every where.
You can create a base class for your UIViewController that can be used across multiple views. iOS will handle the memory management correctly if you load a new view. The old view will be unloaded and
- (void)viewDidDisappear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
will be called. There you can dispose of any excess data.
I am making master detail application, i have dynamic Detail ViewController. Detail ViewController are changed.
But in every Detail ViewController I have one common method updateInfo I want to call that method
Here is my code
UINavigationController *nav=[self.splitViewController.viewControllers objectAtIndex:1];
UIViewController *controller=[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
But it gives me error no method found.
it will work if i use UIViewController name.
HomeViewController *controller=(HomeViewController)[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
But i dnt want to do above things.
I have tried to explain. Please help
You can use id
UINavigationController *nav=[self.splitViewController.viewControllers objectAtIndex:1];
id controller=[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
You could subclass UIViewController and make a base DetailViewController class that houses common functionality of your detail view controllers. Then you would make all of your detail view controllers subclass DetailViewController instead of UIViewController. This would be a safe way to do it and would also allow you to add extra functionality to your updateInfo method in the specific detail view controllers.
If you want an unsafe way, you could make your controller object of type id. I wouldn't suggest this approach as it relies on your personal knowledge of the code. If someone else (or yourself down the road) sets it to a view controller that doesn't have that method, the code will still try to run and will crash.
UIViewController doesn't have a method named updateInfo, so the compiler will of course complain when you try to send that message to a pointer that's known only to point to an instance of UIViewController. When you use the class name, like this:
HomeViewController *controller=(HomeViewController)[nav.viewControllers objectAtIndex:0];
you're providing more information to the compiler, using a type cast to tell it "Hey, don't worry, I know for certain that the object I'll get back is a HomeViewController. Since you seem to have several types of view controllers that all have this method, the best thing to do is to declare the updateInfo method in a protocol and then have each of those UIViewController subclasses implement that protocol. So, your protocol declaration would be in a header file and might look like:
#protocol SomeProtocol
- (void)updateInfo
#end
and each class that has an -updateInfo method would just need to declare that it adopts the protocol:
#interface HomeViewController <SomeProtocol>
//...
#end
and then make sure that you have an -updateInfo in your class implementation:
#implementation HomeViewController
- (void)updateInfo {
//...
}
//...
#end
Then, in your code, you can either check that the object conforms to the protocol using -conformsToProtocol: like this:
if ([controller conformsToProtocol:#protocol(SomeProtocol)]) {
UIViewController<SomeProtocol> *c = (UIViewController<SomeProtocol>*)controller;
[c updateInfo];
}
or else just check that the object responds to the selector before calling it:
if ([controller respondsToSelector:#selector(updateInfo)]) {
[controller performSelector(updateInfo)];
}
The other answers you've received (using id or creating a common base class) are also good ones, but to be safe make sure you do some checking before calling your method. For example, you can use -isKindOfClass to make sure that the view controller you get back is in fact an instance of your common base class, and you can use -respondsToSelector: as above to check that an id points to an object that implements updateInfo.
I am developing an app that makes use of a third party library that presents its own view controller. All that is available to me is a .a library and a header file. My app only runs in portrait mode but when I have the phone in a landscape orientation and present the view controller from the library, the app crashes with an error stating that:
"No supported orientation matches that of the application."
My guess is that they have written the following code:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
// ATTENTION! Only return orientation MASK values
// return UIInterfaceOrientationPortrait;
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeLeft;
}
If that is the case, I will likely need to override those methods to tell the OS not to rotate and that only portrait is supported. How can I go about doing this?
The only possibility I can think of is to swizzle the method for that view controller but this seems like a dangerous approach according to a couple of SO posts.
You can always do the following (as an example of locking a ViewController in Portrait mode):
1) Setup a property on your application delegate header file ... #property (nonatomic) BOOL lockScreenPortraitOnly;
2) In the application delegate implementation file add the following method:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindows:(UIWindow *)window
{
if (!self.lockScreenPortraitOnly)
return UIInterfaceOrientationMaskAll; // or, whatever you wish to support
else
return UIInterfaceOrientationMaskPortrait;
}
3) From within your ViewController add the following:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
((EliotsApplicationDelegateType *)[UIApplication sharedApplication].delegate).lockScreenPortraitOnly = YES;
}
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
((EliotsApplicationDelegateType *)[UIApplication sharedApplication].delegate).lockScreenPortraitOnly = NO;
}
Now, it doesn't matter what's in Plist or what a 3rd party library is doing as the "window" is the most root, and as such, it controls everything else in the hierarchy. Obviously in my example it is assumed that your VC is invoked before your 3rd-party library code gets spun up (adjust, season, and bake as needed).
Hope this helps.
I'd say go ahead and swizzle it. You'll need to be careful when upgrading the library to test this particular item. The danger in swizzling usually comes with swizzling Apple's APIs as they can be deprecated or their behaviour changed at any release (which could subsequently break your app for users).
I have a protocol like this :
#import <Foundation/Foundation.h>
#protocol StoreDisplayerDelegate <NSObject>
-(void) changeActionForObjectWithId:(NSString *)objectID ;
#end
and i have a callass with conforms to the precedent protocol StoreDisplayerDelegate
#interface ShelfVC : UIViewController :<StoreDisplayerDelegate>
....
#implementation ShelfVC
...
- (void)viewDidLoad {
...
DownloadManager *manager = [DownloadManager sharedInstance];
[manager setStoreDisplayerDelegate:self];
....
}
#pragma mark StoreDisplayerDelegate methods
-(void) changeActionForObjectWithId:(NSString *)objectID {
......
}
#end
And in my code ( in the same class) sometimes i am calling the delegate methods to do something form me, for example :
- (void)anOtherMethod{
[self changeActionForObjectWithId:nil];
}
My Questions
1. is : When object is a delegate for an other object, is the methods implemented by the delegate called only by the other object ( witch have a reference for it ) ? i mean by this, for example in the code i have shown should the methode changeActionForObjectWithId: just called by the downLoad manager or can i use it in the inernal of my class like this :
is what i am doing cleaning or bad design of using Delegate pattern ?
I hope that it is clear.
Your delegate method name sounds like a command.
-(void)changeActionForObjectWithId:(NSString *)objectID;
It sounds like your StoreDisplayer is telling delegate to do something. The fact that you are also tempted to call that method from within the ViewController confirms it.
That is not the delegate pattern. The delegate pattern is for a class to inform a delegate of a change, or to ask the delegate for some information. The delegating class (StoreDisplayer?) shouldn't know about what any particular delegate does, so it shouldn't be able to give it direct specific commands. Only delegate generic behaviour to it.
Delegate method look more like these examples:
-(BOOL)actionShouldChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer;
-(void)actionWillChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer objectId:(NSString *)objectId;
-(void)actionDidChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer objectId:(NSString *)objectId;
I'm not saying those are what you need, but they should give you the idea.
When your delegate methods look like this, clearly you will not be tempted to call them from anything other than the class that's doing the delegation (StoreDisplayer).
In my project I have the same situation for different purpose. In my opinion the delegate method must be called only by the other object, because it is its method.
If you need to do certain action in the delegate method, its better to create another private method to performing the action and call it from the delegate methods.
Some example. Instead of doing this:
- (void)anOtherMethod {
[self changeActionForObjectWithId:nil];
}
- (void)changeActionForObjectWithId:(NSString *)objectID {
< some actions >
}
I do this:
- (void)privateMethod{
< some actions >
}
- (void)anOtherMethod {
[self privateMethod];
}
- (void)changeActionForObjectWithId:(NSString *)objectID {
[self privateMethod];
}
Why do this? Because you have to think to the delegate methods to an "extension" to your base object: if you delete the "changeActionForObjectWithId" (becuase, for example, you don't need the delegate anymore after a refactoring) the code will continue to work.
A delegate is a protocol that allows an object to perform certain actions.
In your example:
#interface ShelfVC : UIViewController : <StoreDisplayerDelegate>
This tells the compiler that UIViewController is going to implement some methods in the StoreDisplayerDelegate protocol. How the object that applicable to the StoreDisplayerDelegate behave would depend on the protocol in the delegate methods.
You got a little confused with delegates and protocols. a delegate is a design-pattern wich uses a protocol.
a protocol only defines methods and properties expected to be implemented by another (unspecific) class. from where this methods/properties get accessed doesn't matter.