Can we put IBOutlets in a category? - ios

Since the ViewController's code is getting too large, I was wondering how to split the code into multiple files. Here's the problem I ran into:
// In the original .m file, there are bunch of outlets in the interface extension.
#interface aViewController()
#property (weak, nonatomic) IBOutlet UIView *contentView1;
#property (weak, nonatomic) IBOutlet UIView *contentView2;
#property (weak, nonatomic) IBOutlet UIView *contentView3;
#end
I want to split the file into 3 categories, according to three different views.
// In category aViewController+contentView1.m file
#interface aViewController()
#property (weak, nonatomic) IBOutlet UIView *contentView1;
#end
If I delete the original contentView1 outlet, however, it doesn't work.
Question
Why do I have to keep the contentView1 outlet in the original .m file?

An Objective-C category doesn't allow you to add additional properties to a class, only methods. Thereby, you aren't allowed to add additional IBOutlets within a category. A category is denoted similar to #interface aViewController (MyCategoryName) (note the name given inside the parentheses).
You can, however, add additional properties within a class extension. A class extension is denoted with the same name as the original class followed by (). In your code example, both lines referring to #interface aViewController() actually declare a class extension (not a category), regardless of which header file they're actually in.
Furthermore, you are allowed to create multiple class extensions across several different headers. The trick is that you need to import these correctly.
In example, let's consider a class called ViewController. We want to create ViewController+Private.h and ViewController+Private2.h that have additional privateView outlets, which will still be accessible within ViewController.m.
Here's how we can do it:
ViewController.h
// Nothing special here
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
// some public properties go here
#end
ViewController+Private.h
// Note that we import the public header file
#import "ViewController.h"
#interface ViewController()
#property (nonatomic, weak) IBOutlet UIView *privateView;
#end
ViewController+Private2.h
// Note again we import the public header file
#import "ViewController.h"
#interface ViewController()
#property (nonatomic, weak) IBOutlet UIView *privateView2;
#end
ViewController.m
// Here's where the magic is
// We import each of the class extensions in the implementation file
#import "ViewController.h"
#import "ViewController+Private.h"
#import "ViewController+Private2.h"
#implementation ViewController
// We can also setup a basic test to make sure it's working.
// Just also make sure your IBOutlets are actually hooked up in Interface Builder
- (void)viewDidLoad
{
[super viewDidLoad];
self.privateView.backgroundColor = [UIColor redColor];
self.privateView2.backgroundColor = [UIColor greenColor];
}
#end
And that's how we can do it.
Why Your Code Wasn't Working
Most likely, you've probably mixed up the #import statements. To fix this,
1) Make sure that each class extension file imports the original class header (i.e. ViewController.h)
2) Make sure that the class implementation (i.e. ViewController.m) file imports each of the class extension headers.
3) Make sure the class header (i.e. ViewController.h) file doesn't import any of the class extension headers.
For reference, you can also checkout the Apple docs on Customizing Existing Classes.

Related

Cannot find the Referencing Outlets declared in Objective-C File

I declared a UIView in the objective-C Class File (Test.m)
#property (nonatomic,strong) UIView *Scene1;
In the StoryBoard, when I am trying to make a reference outlet pointing to the view I set in the objective-C file, Scene 1 is not displayed in the list.
Note: 1. I have already pointed the view to the correct class file.
2. They are all UIView type
Here's the code of the header file (Test.h)
#import "JSMessagesViewController.h"
#interface Test : JSMessagesViewController
#end
Here's the code for the Class File Test.m
#import "Test.h"
#import "MessageData.h"
#interface DeerView () <JSMessagesViewDelegate, JSMessagesViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate,UIActionSheetDelegate>
#property (strong, nonatomic) NSMutableArray *messageArray;
#property (nonatomic,strong) UIImage *willSendImage;
#property (nonatomic,strong) UIView *Scene1;
#end
#implementation DeerView
#synthesize messageArray, NameInput, Scene1, Scene2, Scene3, Scene4, Name;
...
#end
I am a high school student and a young developer who is still learning more. Hope you can help with my question.
Change your declaration of outlet and mark it as outlet
#property (nonatomic, weak) IBOutlet UIView *Scene1;
And you don't need to declare it as a strong property if you won't remove it from subview.

Cannot access property from class

//MigrationVC.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface MigrationVC : UIViewController
#end
//MigrationVC.m
#import "MigrationVC.h"
#interface MigrationVC()
#property (strong, nonatomic) IBOutlet UILabel *label;
#property (strong, nonatomic) IBOutlet UIProgressView *progressView;
#end
#implementation MigrationVC
#end
//CoreData
#import "CoreData.h"
#import "MigrationVC.h"
#interface CoreData()
#property (nonatomic,retain) MigrationVC *migrationVC;
#end
-(void)obsererForKeyPath:(NSString*)keyPath object:(id)object change:(NSDictionary*)change context:(void*)context
{
if([keyPath isEqualToString:#"migrationProgress"])
{
dispatch_async(dispatch_get_main_queue(),^{
float progress=[[change objectForKey:NSKeyValueChangeNewKey] floatValue];
self.migrationVC.progress=progress;
});
}
}
I am trying to learn CoreData and migration right now but this is giving me a quite a headache.
I am trying to access the outlet properties from another classes but always gives red warning (Property 'label' not found on object of type MigrationVC*).
I tried adding a NSString property in .h file which was accessible but when i tried to change the outlet from .m to .h file i couldn't ctrl+drag the view in the .h file.
I never had this problem. I have accessed outlet from .m file many times in the past but it just gives me warning now.
How can i access the properties while outlet in .m file?
I cannot outlet the properties in .h file.
You have to transfer you outlet properties from .m file to .h file (copy and paste). If you want your properties to be public so they have to be declared in header file. If you want them to be private - declare them in implementation file.
//MigrationVC.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface MigrationVC : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *label;
#property (strong, nonatomic) IBOutlet UIProgressView *progressView;
#end
//MigrationVC.m
#import "MigrationVC.h"
#interface MigrationVC()
#end
#implementation MigrationVC
#end
As others have pointed out, you need to declare your properties in your header file if you want them to be accessible from other classes. You can, and you should. If you think you can't do that, explain why.
More important, though, is that you should not do what you are trying to do. You should not try to access a view controller's view objects from outside the view controller. That violates the principle of encapsulation, an important principle of object-oriented design. (It means that code outside of your view controller is dependent on the appearance of your view controller. If you later decide to make internal changes to your view controller, you are very likely to break outside code.) In addition to the somewhat abstract "It's bad design" reason for not doing it, it often doesn't work, because at the time to try to modify a view controller's views, they don't exist yet.
Instead, you should create DATA properties (like strings, or floating point progress values) in your view controller and expose those. Then have your view controller's viewWillAppear method install the data into it's views as appropriate. That way the data properties become part of the view controller's public contract without being tied to the internal details of the view controller.
You properties are declared in the private category so they are not visible for other classes. Only properties declared in a header file are visible.
In CoreData.m your MigrationVC is set as retain. I don't know if you can use it since with ARC.
#property (nonatomic,retain) MigrationVC *migrationVC;
It should be
#property (nonatomic,strong) MigrationVC *migrationVC;
And in MigrationVC your outlets should be weak not strong and in .h to be accesible from CoreData.m

When creating a property for an instance of a viewController, the viewController cannot be found

I am trying to create a property for an instance of a viewController, like so,
#property (strong, nonatomic) DetailsViewController *videoViewController;
but I get an error saying that:
DetailsViewController is an unknown type name
The view controller is definitely imported like so,
#import "DetailsViewController.h"
Why is this happening?
To avoid Circular imports, always write Import statements in .m file, and use forward declaration in .h file.
In .h file
#class DetailsViewController;
In .m file
#import "DetailsViewController.h"
OR
For private properties, use Objective - C extensions, i.e,
Im .m file
#import "DetailsViewController.h"
#interface MasterViewController ()<YourProtocolList>
#property(nonatomic, strong) DetailsViewController *detailViewController;
#end
#implementation MasterViewController
//Your implementation stuff
#end
In case of inheritance, you may need to import in .h file.

Objective-C: How do I access parent private properties from subclasses?

//Super class .h file
#interface MySuperClass : NSObject
#end
//Super class .m file
#interface MySuperClass ()
#property (nonatomic, strong) UITextField *emailField;
#end
#implementation MySuperClass
-(void)accessMyEmailField {
NSLog(#"My super email: %#", self.emailField.text);
}
#end
// ********** my subclass *******
//Subclass .h file
#interface MySubClass : MySuperClass
#end
//SubClass .m file
#interface MySubClass ()
#end
#implementation MySubClass
-(void)myEmail {
NSLog(#"My subclass email: %#", self.emailField.text);
}
-(void)setMyEmailFromSubclass{
self.emailField.Text = #"email#gmail.com"
}
#end
How do i access emailField in -(void)myEmail method.
How do i set email in Subclass -(void)setMyEmailFromSubclass; , and access it in super class accessMyEmailField
You can put accessors to these properties in a second header file, and import that file on a 'need-to-know' basis..
eg
mySuperClass+undocumentedProperties.h
#import "mySuperClass.h"
#interface mySuperClass(undocumentedProperties)
#property (nonatomic, strong) UITextField *emailField;
#end
mySuperClass.m
#import "mySuperClass+undocumentedProperties.h"
#interface mySuperClass()
///stuff that truly will be private to this class only
// self.emailField is no longer declared here..
#end
#implementation mySuperClass
#synthesize emailField; //(not really needed anymore)
/// etc, all your code unaltered
#end
mySubclass.h
#import "mySuperClass.h"
#interface mySubclass:mySuperClass
///some stuff
#end
mySubclass.m
#import "mySubclass.h"
#import "mySuperClass+undocumentedProperties.h"
#implementation
//off you go, this class is now 'aware' of this secret inherited property..
#end
obviously MySuperClass.m will have to import this .h file as well as its default one (or actually instead of, the default one is built in to this one), but your subclasses can import it too (directly into their .m file, so these properties remain private to the class. This is not a proper category because there is no corresponding mySuperClass+undocumentedProperties.m file (if you tried that you could not synthesize the backing iVars for these secret properties. Enjoy :)
Copy the private interface portion of the methods you want from your superclass - or in other words, in your Subclass.m file you would put:
#interface MySuperClass ()
#property (nonatomic, strong) UITextField *emailField;
#end
( place it above the existing #interface MySubClass () code )
Now your subclass knows that method exists in the superclass and can use it, but you are not exposing it to anyone else.
The whole point of private properties is exactly that and you should not want to access them. Because they are private they can change or be removed thus breaking the subclass that relies on them.
That being said they are not really private, just not "published". The can be called because Objective-C is a run-time dynamic language.

Properties across classes in Objective C

I am new to Objective C, so please forgive the simplicity of my question.
I have a class, lets call it Class A and it looks like this
#interface A()
#property (weak, nonatomic) IBOutlet UISegmentedControl *segment;
#end
#implementation A
...
Now what I want to do, is "get" the segment in class B. I have tried doing this by doing this
#interface B()
#property (nonatomic) B *cardy;
#end
#implementation B
...
I then want to do cardy.segment, but I cannot seem to access it. I tried using #synthesize in A but need to ensure exactly how to implement the "get" properly. Any advice would be greatly appreciated.
Declare the property in the .h file, not the .m file. Also, make sure A.h is imported in B.m

Resources