game.h file
#interface ViewController : UIViewController
{
UIImageView * player;
}
#end
game.m
-(void)InitPlayer
{
player.tag = 0;
}
player.m
I want InitPlayer in this file instead but keep declaration in game.h file.
- (void)InitPlayer
{
player.tag = 0; // access UIImageView *player;
}
Is this possible to do?
Thanks
just use this to declare -InitPlayer part of your class' public interface:
#interface ViewController : UIViewController
{
#private
UIImageView * player;
}
- (void)InitPlayer;
#end
then you keep your implementation of -InitPlayer in ViewController.m, where it must reside. You cannot place the method's implementation inside an #interface block.
if you are coming from other languages, then you should know that dispatch in objc is dynamic -- placing a definition in the header (if it were possible) would not result in an inlined/optimization.
In response to the clarified question:
Ok, then just do this:
Player.h
#interface Player : NSObject
- (void)prepareUIImageView:(UIImageView *)pImageView; // << could actually be a class method in this case
#end
Player.m
#implementation Player
- (void)prepareUIImageView:(UIImageView *)pImageView
{
pImageView.tag = 0;
}
#end
then you can tell the Player to initialize the view from the ViewController, assuming it has no controller itself.
(note: objc methods names typically begin with a lowercase character. as well, it's usually a good idea to hide this initialization stuff from your clients)
Related
I am trying to access an Objective C singleton from Swift, however I only seem to get the initial value created in the init function of the singleton. The flightControllerState object exposed is updated in a delegate function and I can see that the value is properly updated on the Objective C side.
I have followed a few different posts here on SO and also this article on how to call the shared object from Swift. (I should also mention this is running inside a react native project if that may have any impact?)
EDIT updated swift code - I added the wrong line to the init method to grab shared instance - issue is still the same
Objective-C Singleton
#import DJISDK;
#interface RCTBridgeDJIFlightController : RCTEventEmitter<DJIFlightControllerDelegate> {
DJIFlightControllerState *flightControllerState;
}
#property(nonatomic, readonly) DJIFlightControllerState *flightControllerState;
+ (id)sharedFlightController;
#end
#implementation RCTBridgeDJIFlightController
DJIFlightControllerState *flightControllerState;
#synthesize flightControllerState;
+ (id)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
- (id)init {
// I also tried this to make sure the shared instance was returned but no luck
//if (sharedFlightControllerInstance != nil) {
// return sharedFlightControllerInstance;
//}
if (self = [super init]) {
flightControllerState = nil;
}
return self;
}
-(void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
flightControllerState = state;
}
#end
Swift class calling singleton and accessing values
class VirtualStickController {
var flightControllerSharedInstance: RCTBridgeDJIFlightController
override init() {
self.flightControllerSharedInstance = RCTBridgeDJIFlightController.sharedFlightController()
}
func getFlightControllerState() {
if let state = flightControllerSharedInstance.flightControllerState {
print("FLIGHT CONTROLLER STATE: \(state)") // always null
} else {
print ("NULL")
}
}
DJIFlightControllerState *flightControllerState;
#synthesize flightControllerState;
There is no need to use #synthesize for properties in (modern) Objective-C except in special circumstance.
The property flightControllerState is an instance property and will be synthesised (with or without the #synthesize) using a hidden instance variable for its storage.
The variable flightControllerState is a global variable, it happens to have the same name as the property but has no connection whatsoever with it.
At a guess you are changing the global variable in Objective-C and expecting to see the result in Swift via the property, you won't.
Remove the global variable and then check the rest of your code.
Apart from that your code produces a valid shared instance which can be shared between Objective-C and Swift and changes made in one language will be visible in the other.
HTH
Regarding the titular question about how to access an Objective C singleton from Swift, I would recommend an alternative. Modern convention is to declare your sharedFlightController as a class property and declare init as NS_UNAVAILABLE:
#interface RCTBridgeDJIFlightController : NSObject
...
#property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
#end
The implementation would implement a getter for this class property:
#implementation RCTBridgeDJIFlightController
+ (instancetype)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
...
#end
Now, your Swift code can reference RCTBridgeDJIFlightController.shared, as is the convention with Swift singletons.
Regarding why you are receiving a nil for the status, there are one of two possible problems:
You Objective-C code has confusing combination of explicitly defined ivars, manual synthesis, and global variables. (See below.)
I would also suggest that you confirm whether flightController:didUpdateState: is ever getting called at all. (I don't see you ever setting the delegate of the flight controller.) Add a breakpoint or NSLog statement in that method and confirm.
On the first issue, above, I would suggest:
You should not use those commented lines in your init method. If you want to make sure that your singleton object is used, then declare init as NS_UNAVAILABLE.
Given that all your init method is doing is updating flightControllerState to nil, you can remove it entirely. In ARC, properties are initialized to nil for you.
You should not declare explicit ivar in your #interface. Let the compiler synthesize this automatically for you.
You should not #synthesize the ivar in your #implementation. The compiler will now automatically synthesize for you (and will use an appropriate name for the ivar, adding an underscore to the property name.
You should not declare that global in your #implementation.
If you want to use this sharedFlightController from Swift, you should define it to be a class property, not a class method. I know that that article suggested using a class method, but that really is not best practice.
Thus:
// RCTBridgeDJIFlightController.h
#import <Foundation/Foundation.h>
// dji imports here
NS_ASSUME_NONNULL_BEGIN
#interface RCTBridgeDJIFlightController : NSObject
#property (nonatomic, readonly, nullable) DJIFlightControllerState *flightControllerState;
#property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
#end
NS_ASSUME_NONNULL_END
And
// RCTBridgeDJIFlightController.m
#import "RCTBridgeDJIFlightController.h"
#interface RCTBridgeDJIFlightController ()
#property (nonatomic, nullable) DJIFlightControllerState *flightControllerState;
#end
#implementation RCTBridgeDJIFlightController
+ (instancetype)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
- (void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
NSLog(#"State updated");
self.flightControllerState = state;
}
#end
The end result is that you can now use it like so:
class VirtualStickController {
func getFlightControllerState() {
if let state = RCTBridgeDJIFlightController.shared.flightControllerState {
print("FLIGHT CONTROLLER STATE: \(state)")
} else {
print("NULL")
}
}
}
Note, because the sharedFlightController is now a class property, Swift/ObjC interoperability is smart enough so the Swift code can just reference shared, as shown above.
i want to know if the following situation can be done. I have inherited a project of iOS 8. I'd like to add a method to NSObject so that all objects can see it. and I have done this already. Here is the category implementation i have created:
#import "NSObject+myCategory.h"
#implementation NSObject (myCategory)
-(void)localizeStringIncludeDefault{
NSLog(#"about to localize String");
}
#end
Now i go to a MyViewController.m for example and try to call the method but its not working its not seen:
heres the .h:
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController {
BOOL someFakeBoolean;
IBOutlet UIView *someView;
//etc
}
#property (nonatomic,assign) IBOutlet MyViewController *myViewController;
-(void)localizeStringIncludeDefault;
and here is the implementation *.m and my issue:
#implementation MyViewController
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self localizeStringIncludeDefault] //this call cant be done, the method is not visible
}
- (void) viewDidDisappear:(BOOL)animated
{
//etc
}
//etc
I mean it makes sense to me that i'd have to import the "NSObject+myCategory.h" into the MyViewController header to use the category but because i've inherited this code it already has a base. I dont want to go into every .h file and import this. Is a easy way to extend object so that EVERY object class sees my method ?
One option would be to add the category .h file to the pch file. Then it will be seen by every class in your project without the need to import it explicitly.
Declare your global variables or declarations in your pch file or rather make a Global.h and just import this in your pch file (helps a lot in reusability). You can declare extern items as well in your Global.h and populate in App Delegate
I am attempting to create an abstract class and inherit some of its properties in a subclass. If I leave the properties in the abstract class' header file, all of the properties are accessible. The problem is that the instance of the subclass can also access those properties, which is not always desirable in my case.
For instance, I have a delegate in my abstract class that sends down button presses to its sub class. I realize that this may not be the best way of structuring inheritance, so other suggestions are welcome. However, I would still like to know how my subclass can inherit some properties from its superclass without making all of those properties available in its instance. Thanks in advance!
Here is some example code below:
#interface AbstractClass : UIView
#property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;
#end
…
#protocol ButtonDelegate
#required
- (void) buttonWasPressed;
#end
…
#interface SubClass() <ButtonDelegate>
- (id)init {
self = [super init];
if (self) {
self.buttonDelegate = self;
}
return self;
}
-(void) buttonWasPressed {
[self doSomething];
}
…
#implementation ViewController
- (void)viewDidLoad {
SubClass *subClass = [[SubClass alloc] init];
subClass.buttonDelegate = self; // THIS IS NOT DESIRABLE
}
Do like UIGestureRecognizer does.
All public properties and methods goes into UIGestureRecognizer.h
All protected properties and methods goes into UIGestureRecognizerSubclass.h.
Only import this in the *.m-files. Never include it in any public header.
All private properties and methods goes into *.m-files. Use the #interface ClassName ()
Example https://gist.github.com/hfossli/8041396
how to my subclass can inherit some properties from its superclass
without making all of those properties available in its instance
What is the problem with this?
#import <Foundation/Foundation.h>
#interface Animal : NSObject
{
#protected
NSString *name; // default access. Only visible to subclasses.
}
#end
#implementation Animal
-(NSString*)description {
return name;
}
#end
#interface Cow : Animal
#end
#implementation Cow
-(id)init {
self=[super init];
if (self){
name = #"cow";
}
return self;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Cow *cow = [Cow new];
NSLog(#"%#", cow); // prints the name through internal access
// error accessing from the outside: NSLog(#"%#", cow.name);
Animal *animal = [Animal new];
// error accessing from the outside: NSLog(#"%#", animal.name);
}
}
Maybe I misunderstood the question, you say
Creating properties only visible to subclass in Objective-C
and then
The problem is that the instance of the subclass can also access those
properties
Which one is it?
Create an empty category on top of your implementation file (.m):
#interface AbstractClass()
#property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;
#end
In that way, your subclass will inherit and can access that property, but not other external classes because it's not in the header.
I don't think there is any way to achieve this using property declaration.
Either a property be visible for all (declared in .h file) or it will be invisible for all (declared in .m file using category)
I guess one way is declaring public/protected variable in .h file class declaration:
#interface AbstractClass : UIView {
...
id<ButtonDelegate>buttonDelegate;
...
}
#end
I am not sure about this, but give a try.
I see one approach that can fit your problem, however, it is pretty rude. Use Antonio's suggestion and create the private category with the property. As you've mentioned, it's scope is limited to the .m file. So you can put your subclasses into that file. This will be hard to read the code if subclasses are huge, but this is the only way for you as far as I understand.
EDIT: well, I have another solution. Copy
#property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;
to all your subclasses. This will give you a warning about the absence of the property's #synthesize, but should work. I'd prefer this, if subclasses wont be changed or added often.
Let me describe how it would work.
We add a property into the Abstract class, and it is hidden for all (even for subclasses):
// .m file
#interface Abstract ()
#property (nonatomic, strong) id<ButtonDelegate> buttonDelegate;
#end
#implementation Abstract
#synthsize buttonDelegate;
#end;
But due to runtime features of Objective-C we still can call for that property, and there will not be any runtime error, only compiler warning.
To get rid of that warning and to add an ability to autocomplete, we add property without #synthsize into all subclasses:
#interface MySubclass : Abstract
#property (nonatomic, strong) id<ButtonDelegate> buttonDelegate;
#end
This will tell the compiler that there is such a property somewhere. There will be also one warning about the absence of #synthesize, but Xcode will still could autocomplete if you write something like
MySubclass *subclass = ...
subclass.butto...
It can not be done. There is no private or protected in objective-c. Stuff declared in the .m file "private" interface is only visible to that class and not in any subclass. Also you can always use your "private" properties/methods from outside if you want, although it would be bad practice to do so.
I would like to set up my global constant values within a Constants Singleton class in my iOS app such that any class importing the constants can use those values.
However, after playing around for a few hours with this idea, I am still unable to make it work.
In my Constants.m file
#interface Constants()
{
#private
int _NumBackgroundNetworkTasks;
NSDateFormatter *_formatter;
}
#end
#implementation Constants
static Constants *constantSingleton = nil;
//Categories of entries
typedef enum
{
mapViewAccessoryButton = 999
} UIBUTTON_TAG;
+(id)getSingleton
{
.....
}
I have another class MapViewController where I have a reference to the Constants singleton and Im trying to access the enums like this
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
However, this is not working. Im not able to access the UIBUTTON_TAG inside the mapviewcontroller
ANybody have any suggestions?
Thanks
If you want the enum available throughout the app, put the enum definition in the .h file, not the .m file.
Update:
Objective-C doesn't support namespaces and it doesn't support class level constants or enums.
The line:
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
should be:
myDetailButton.tag = mapViewAccessoryButton;
assuming you define the UIBUTTON_TAG enum in some .h file.
When you compile an Objective-C app, all values of all enum must have unique names. This is a result of Objetive-C being based on C.
Update 2:
There is one way to get what you want but not with enums. Something like this should work:
Constants.h:
#interface UIBUTTON_TAG_ENUM : NSObject
#property (nonatomic, readonly) int mapViewAccessoryButton;
// define any other "enum values" as additional properties
#end
#interface Constants : NSObject
#property (nonatomic, readonly) UIBUTTON_TAG_ENUM *UIBUTTON_TAG;
+ (id)getSingleton;
// anything else you want in Constants
#end
Constants.m
#implementation UIBUTTON_TAG_ENUM
- (int)mapViewAccessoryButton {
return 999;
}
#end
#implementation Constants {
int _NumBackgroundNetworkTasks;
NSDateFormatter *_formatter;
UIBUTTON_TAG_ENUM *_uiButtonTag;
}
#synthesize UIBUTTON_TAG = _uiButtonTag;
- (id)init {
self = [super init];
if (self) {
_uiButtonTag = [[UIBUTTON_TAG_ENUM alloc] init];
}
return self;
}
// all of your other code for Constants
#end
Now you can do:
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
I'm not sure if there is a point to this though.
One way to do this is simply stick it in your precompiled header (.pch) if you aren't going to be changing the enum a lot.
In .h file
#interface ViewController : UIViewController {
#private int intVariable1;
}
#property (readwrite,assign) int iVar;
-(void)Callme;
#end
In .m file
#implementation ViewController
#synthesize iVar=intVariable1;
-(void)Callme
{
NSLog(#”Callme called”);
}
-(void)setIVar:(int)inIVar
{
intVariable1 = inIVar;
[self Callme];
}
#end
I have implement this code to call "Callme" function when the variable state changes, But when i call the function setIVar in viewDid load it does not work, Any idea of calling this?
Way i used to call
[self setIVar:3];
If you want to provide your own implementation for getter/setter functions do not use the #synthesize directive in your implementation! This will generate its own -(void)setIVar:(int)inIVar and -(int)iVar methods and these might hide your version. In turn, if you want a readwrite property, you'll also have to implement the getter i.e. -(int)iVar.
Simple remove the #synthesize iVar=intVariable1; line from your implementation and put implement also -(int)iVar.