Custom classes & casting inside conditional statements in Objective-C - objective-c

I ran into this issue twice now, and am curious to know if there is a correct way of getting the following example to work. I know there are other ways /workarounds for doing the same thing but I am wondering why the compiler doesn't recognise my casting and if I am missing something obvious here.
Suppose I have two table views with different style header views that I need to provide. SectionHeaderViewA is a UIView subclass with a custom property textLabelA, SectionHeaderViewB is also a UIView subclass with a custom property textLabelB.
In the method:
- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
id headerView;
if (tableView.tag == TAG_A)
{
headerView = (SectionHeaderViewA*)[[SectionHeaderViewA alloc] init];
headerView.textLabelA = ... // I am unable to access the custom property here even after casting (SectionHeaderViewA*) above.
} else if (tableView.tag == TAG_B) {
headerView = (SectionHeaderViewB*)[[SectionHeaderViewB alloc] init];
headerView.textLabelB = ... // Same as above, even after casting the custom property is not recognised
}
return headerView;
}
Even after casting (SectionHeaderViewA*) and (SectionHeaderViewB*) to my headerView ivar, I am still unable to access their respective custom properties. It's like the compiler is still seeing headerView as an unknown / id type but why?

The cast is not in the correct place. Cast headerView before sending the appropriate textLabel A or B message.
id headerView;
if (tableView.tag == TAG_A)
{
headerView = [[SectionHeaderViewA alloc] init];
((SectionHeaderViewA*)headerView).textLabelA = ... // I am unable to access the custom property here even after casting (SectionHeaderViewA*) above.
} else if (tableView.tag == TAG_B) {
headerView = [[SectionHeaderViewB alloc] init];
((SectionHeaderViewB*)headerView).textLabelB = ... // Same as above, even after casting the custom property is not recognized
}
return headerView;
Once you move the cast, you will be able to send the correct message.

the type of headerView is "id", which means it doesn't know about your extra properties etc (the cast doesn't change the type of "headerView").
you could do something like:
if (tableView.tag == TAG_A)
{
SectionHeaderViewA* headerView = [[SectionHeaderViewA alloc] init];
headerView.textLabelA = ...
return headerView;
} else if (tableView.tag == TAG_B) {
SectionHeaderViewB* headerView = [[SectionHeaderViewB alloc] init];
headerView.textLabelB = ...
return headerView;
}
return nil;

Your cast is not doing anything as you are casting into id.
Whilst #sean's answer works and it does the single exit it is pretty ugly having all the curly brackets I would probably go for
id headerView = nil; // Initialize to nil... you may not go into either side of your if
if (TAG_A == tableView.tag) {
SectionHeaderViewA *sectionHeaderViewA = [[SectionHeaderViewA alloc] init];
sectionHeaderViewA.textLabelA = ...
headerView = sectionHeaderViewA;
} else if (TAG_B == tableView.tag) {
SectionHeaderViewB *sectionHeaderViewB = [[SectionHeaderViewB alloc] init];
sectionHeaderViewB.textLabelB = ...
headerView = sectionHeaderViewB;
}
return headerView;
Or another possibility (potentially over engineering the problem) is make both sectionHeaderViewA and sectionHeaderViewB conform to a protocol and then you can make it a little tidier still.
SectionHeaderInterface.h
#protocol SectionHeaderInterface <NSObject>
#property (strong, nonatomic) UILabel *textLabel;
#end
SectionHeaderView(A|B).h
#import "SectionHeaderInterface.h"
#interface SectionHeaderView(A|B) : UIView <SectionHeaderInterface>
// ... rest of interface
#end
SectionHeaderView(A|B).m
#implementation SectionHeaderView(A|B)
#synthesize textLabel = _textLabel;
// ... rest of your class
#end
YourController.m
id<SectionHeaderInterface> headerView = nil; // Initialize to nil... you may not go into either side of your if
if (TAG_A == tableView.tag) {
headerView = [[SectionHeaderViewA alloc] init];
} else if (TAG_B == tableView.tag) {
headerView = [[SectionHeaderViewB alloc] init];
}
headerView.textLabel.text = ...
return headerView;

Related

Accessing Property from another NSObjectClass

I am using 2 nsobject class ,
First nsobject class some property and it holds some values.
In Second NSObject class i want to access the first nsobject class properties.
I have tried little bit ,it showing null values
Here is my code
My First NSObject class
FieldData.M
-(instancetype)initWithDictionary:(NSDictionary *)dictFieldData
{
if (self = [super init]) {
///set the field id
self.fieldId = [[dictFieldData objectForKey:#"f_id"]intValue];
self.display = [[dictFieldData objectForKey:#"f_display"]boolValue];
self.fieldLength = [[dictFieldData objectForKey:#"f_length"]intValue];
self.mandatory = [[dictFieldData objectForKey:#"f_mandatary"]boolValue];
self.strFieldLabel = [dictFieldData objectForKey:#"f_label"];
NSString *strFieldAttrib = [dictFieldData objectForKey:#"f_attribute"];
if ([strFieldAttrib.lowercaseString isEqualToString:#"alpha"]) {
self.fieldAttribute = FieldAttributeAlpha;
}
else if ([strFieldAttrib.lowercaseString isEqualToString:#"numeric"]) {
self.fieldAttribute = FieldAttributeNumeric;
}
else if ([strFieldAttrib.lowercaseString isEqualToString:#"alpha_numeric"]) {
self.fieldAttribute = FieldAttributeAlphaNumeric;
}
}return self;
}
****Second NSObjectClass**
SiteData.m**
-(instancetype)initWithDictionary:(NSDictionary *)dictSiteData
{
self.fieldData = [FieldData new];
if (self = [super init]) {
self.siteId = [[dictSiteData objectForKey:#"s_id"]intValue];
self.siteName = [dictSiteData objectForKey:#"site_name"];
NSLog(#"%d",self.fieldData.fieldId);
}
return self;
}
Please anyone helpme to do this
Please what i am doing wrong
Thanks in Advance !!!
Please write this code in Appdelegate declare this property.
After that SetValue your Dictionary Data
#property(strong,nonatomic)NSDictionary * dictFieldData;
Write this code My First NSObject class FieldData.M
Create object in ViewdidLoad
AppDelegate *appdelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate]);
///set the field id
self.fieldId = [[appdelegate.dictFieldData SetValue:(NSString*) objectForKey:#"f_id"]intValue];
///Get the field id
self.siteId = [[appdelegate.dictSiteData Valueforkey:#"s_id"]intValue];
NSLog(#"%d",appdelegate.dictSiteData);
Check this post answer for Accessing Property from another NSObjectClass
[FieldData new] will by default invoke the init method, not the initWithDictionary one. Unless you've defined init to set values for your property it will default to nil. You probably need to create your object using [[FieldData alloc] initWithDictionary:...] (or [FieldData newWithDictionary:...] if you've defined that method). HTH

Swift's "if let" equivalent in Objective C

What would be "if let" equivalent in Objective C? The example snippet I want to convert to Objective C is below;
if let pfobjects = images as? [PFObject] {
if pfobjects.count > 0 {
var imageView: PFImageView = PFImageView()
imageView.file = pfobjects[0] as! PFFile
imageView.loadInBackground()
}
}
There's no direct equivalent to if let in Objective-C, because if let does Swift-specific things (unwrapping optionals and rebinding identifiers) that don't have direct equivalents in Objective-C.
Here's a nearly equivalent of your Swift code:
if (images != nil) {
NSArray<PFObject *> *pfobjects = (id)images;
if (pfobjects.count > 0) {
PFImageView *imageView = [[PFImageView alloc] init];
assert([pfobjects[0] isKindOfClass:[PFFile class]]);
imageView.file = (PFFile *)pfobjects[0];
[imageView loadInBackground];
}
}
But this Objective-C code won't verify that images only contains instances of PFObject, and should successfully create an image view as long as pfobjects[0] is a PFFile. Your Swift code will do nothing (create no image view) if images contains any non-PFObject elements.
You can use NSPredicate to verify the array contains only instances of PFObject:
NSPredicate *p = [NSPredicate predicateWithFormat:#"self isKindOfClass: %#", [PFObject class]];
NSInteger numberThatArePFObjects = [images filteredArrayUsingPredicate:p].count;
if(numberThatArePFObjects && numberThatArePFObjects == images.count){
// certain that images only contains instances of PFObject.
}
If however you weren't working with an array but a single object then it is simpler:
if([image isKindOfClass:[PFObject class]]){
// certain that image is a valid PFObject.
}
Or if you wanted a new variable:
PFObject* obj = nil;
if([image isKindOfClass:[PFObject class]] && (obj = image)){
// certain that obj is a valid PFObject.
}
You can use something like this:
NSArray<PFObject *> *pfobjects;
if ([images isKindOfClass: [NSArray<PFObject> class]] && (pfobjects = images)) {
// your code here
}
You want three things simultaneously. Let's split them:
variable as? OtherType is possible, but erases type, because it returns id. Implementation is as easy as a category on NSObject class, so it becomes NSArray *array = [jsonDict[#"objects"] ifKindOfClass:NSArray.class].
Implementation
#interface NSObject (OptionalDowncast)
- (id)ifKindOfClass:(__unsafe_unretained Class)clazz;
#end
#implementation NSObject (OptionalDowncast)
- (id)ifKindOfClass:(__unsafe_unretained Class)clazz {
return [self isKindOfClass:clazz] ? self : nil;
}
#end
if let is also possible in Objective-C if type is known, so it cannot be combined with previous thing. Easiest way is: for(NSArray *array = [self getItems]; array != nil; array = nil) { ... }, but if you want to use else branch, it gets a bit more complex. I have made SwiftyObjC pod for that, please take a look
Check generic template is not possible during type cast in Objective-C, thus you can cast to NSArray, but you can't cast to NSArray<PFObject>
I don't see iterations over your array: With all that being said, I think best example is (assuming images is an array already):
for(PFFile *file = [images.firstObject ifKindOfClass:PFFile.class]; file != nil; file = nil) {
imageView.file = file;
[imageView loadInBackground];
}
If you need to also iterate over it:
for(id object in images) {
for(PFFile *file = [object ifKindOfClass:PFFile.class]; file != nil; file = nil) {
//operate on file
}
}
You can use Objective-C++ in place of Objective-C. In this you can use the next define:
#define let const auto
Note: it is not the same exactly (Swift has wrapped values, ...) but it makes the work easier.
And through of this define you can use it of this way:
if (let pfobjects = images) {
if (pfobjects.count > 0 {
let imageView = [[PFImageView alloc] init];
imageView.file = pfobjects[0];
imageView loadInBackground();
}
}
To convert your Objective-C class in Objective-C++ class you only must change the extension of implementation file of .m to .mm

NSMutableDictionary - entries appear to be over-written by each addition

I'm fairly new to Objective-C; but have been coding for years and this one really stumps me.
I'm trying to build an iPhone app and wanted to create a "settings" screen which will use a Table format. (Xcode 5.1.1).
I want to future proof the main Settings screen and make it easy for the application coding by hiding the "hard work" in subroutines/methods.
I may be getting too clever but I've created a class for each 'setting' that contains screen prompts, default values etc and using an Enum to cross-reference it (so the compiler will highlight typos etc)
The problem I'm encountering is that when I add entries to my NSMutableDictionary and use lldb to print the values; every entry seems to have the same "key" and values. I've tried converting the eNum to an NSNumber and also as an NSString -- no difference in the result - so I'm obviously doing something else daft but just can't see it
The following code is from various .m & .h files, I've omitted boring stuff that you always "have to have" to keep it short
// basic x-ref I want to use in my code
typedef NS_OPTIONS(NSInteger, ConfigurationType) {
unDefined = -1,
Server = 0,
Id = 1,
Phone = 2
};
// definition for a "single" Settings value
#interface SettingDefinition : NSObject
#end
#implementation SettingDefinition
ConfigurationType _cfgType;
NSString *_cfgName;
NSString *_screenTitle;
NSString *_value;
- (NSString *)description
{
NSString *className = NSStringFromClass([self class]);
return [NSString stringWithFormat:#"<%#: x%p Type=%d dbKey=%# '%#' -> %#>", className, self, _cfgType, _cfgName, _screenTitle, _value];
}
- (id)initType:(ConfigurationType)cfgOption
withDbKey: (NSString*)dbKey
asOptionTitle:(NSString*)cfgTitle
withValue:(NSString*)itmValue
{
self = [super init];
if (self) {
_screenTitle = cfgTitle;
_cfgName = dbKey;
_cfgType = cfgOption;
_value = itmValue;
}
return self;
}
#end
#interface Configuration : NSObject
#end
#implementation Configuration {
NSMutableDictionary *Settings; // List of Setting structures
};
- (id)init {
self = [super init];
if (self) {
Settings = [[NSMutableDictionary alloc]init];
[self add:Server withDbKey:#"Server" asOptionTitle:#"Server"];
[self add:Id withDbKey:#"Id" asOptionTitle:#"Your ID"];
[self add:Phone withDbKey:#"Phone" asOptionTitle:#"Phone No."];
}
return self;
}
- (void) add:(ConfigurationType)cfgOption
withDbKey:(NSString*)dbKey
asOptionTitle:(NSString*)cfgTitle
{
NSString * itmValue = [self configurationValue: cfgOption cfgName:dbKey];
SettingDefinition *x = [[SettingDefinition alloc]
initType: cfgOption
withDbKey: dbKey
asOptionTitle: cfgTitle
withValue: itmValue];
[Settings setObject:x forKey:[self asKey:cfgOption]];
}
- (NSString *) asKey:(ConfigurationType) settingType {
NSString *rc = [NSString stringWithFormat:#"%d", settingType];
return rc;
}
- (NSString *) configurationValue:(ConfigurationType) settingType {
// returns a suitable value from my system setup
// which is initially a null value until the user sets everything up
}
the debug window shows the following when I break after the final call to [self add: ...]
(lldb) po Settings
{
0 = "<SettingDefinition: x0x8e7c280 Type=2 dbKey=Phone 'Phone No.' -> (null)>";
1 = "<SettingDefinition: x0x8c703a0 Type=2 dbKey=Phone 'Phone No.' -> (null)>";
2 = "<SettingDefinition: x0x8e7c310 Type=2 dbKey=Phone 'Phone No.' -> (null)>";
}
The (null) is obviously due to no data in 'value' yet; but why do they all show as 'Phone'; if I break after the second call to [self add:..] they all show as 'Id'
UPDATE:
DOH! obviously they're globals (I've been using another IDE where everything is local until exposed) .. If I enclose them in braces in the implementation as the documentation states then the exhibited problem vanishes. I have properties to access the variables but as the setter does more than just set the memory, I thought I'd need my "own" variables to hold the data.. said it was something daft .. thank you!

Dynamic Accessibility Label for CALayer

How do I make a CALayer accessible? Specifically, I want the layer to be able to change its label on the fly, since it can change at any time. The official documentation's sample code does not really allow for this.
The following assumes that you have a superview whose layers are all of class AccessableLayer, but if you have a more complex layout this scheme can be modified to handle that.
In order to make a CALayer accessible, you need a parent view that implements the UIAccessibilityContainer methods. Here is one suggested way to do this.
First, have each layer own its UIAccessibilityElement
#interface AccessableLayer : CALayer
#property (nonatomic) UIAccessibilityElement *accessibilityElement;
#end
now in its implementation, you modify the element whenever it changes:
#implementation AccessableLayer
... self.accessibilityElement.accessibilityLabel = text;
#end
The AccessableLayer never creates the UIAccessibilityElement, because the constructor requires a UIAccessibilityContainer. So have the super view create and assign it:
#pragma mark - accessibility
// The container itself is not accessible, so return NO
- (BOOL)isAccessibilityElement
{
return NO;
}
// The following methods are implementations of UIAccessibilityContainer protocol methods.
- (NSInteger)accessibilityElementCount
{
return [self.layer.sublayers count];
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
AccessableLayer *panel = [self.layer.sublayers objectAtIndex:index];
UIAccessibilityElement *element = panel.accessibilityElement;
if (element == nil) {
element = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
element.accessibilityFrame = [self convertRect:panel.frame toView:[UIApplication sharedApplication].keyWindow];
element.accessibilityTraits = UIAccessibilityTraitButton;
element.accessibilityHint = #"some hint";
element.accessibilityLabel = #"some text";
panel.accessibilityElement = element;
}
return element;
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
int numElements = [self accessibilityElementCount];
for (int i = 0; i < numElements; i++) {
if (element == [self accessibilityElementAtIndex:i]) {
return i;
}
}
return NSNotFound;
}

Objective C enumeration type in object changing on reassignment

I have searched and can't find the assert to this. I know it must be a fundamental thing I'm missing here.
I have an enum:
typedef enum {
NINETYBEND, NINETYBEND_FAST, NINETYBEND_SLOW, STRAIGHT
} BlockTypeEnum;
I am trying to use these values in objects I am creating like this:
BlockTypeEnum blockType = STRAIGHT;
XMLLevelPiece* piece = [[XMLLevelPiece alloc] init];
[piece initPiece:blockType];
My problem occurs when I try to use the same variable twice. If I create one object with an enum, change the enum and then create a second object using it, the enum in my first object changes to the second enum value. This is not what I want. Example below:
BlockTypeEnum blockType = STRAIGHT;
XMLLevelPiece* piece = [[XMLLevelPiece alloc] init];
[piece initPiece:blockType];
blockType = NINETYBEND_FAST;
XMLLevelPiece* piece2 = [[XMLLevelPiece alloc] init];
[piece2 initPiece:blockType];
NSLog([NSString stringWithFormat:#"%d", [piece getBlockType]]);
NSLog([NSString stringWithFormat:#"%d", [piece2 getBlockType]]);
//BOTH BLOCK TYPES ARE NOW NINETYBEND_FAST, NOT WHAT I WANTED!!
As far as I understood, an enum is just a glorified int, not a pointer, and I am reassigning the variable after adding to the first object. Please can someone tell me what I'm missing! Thanks very much, Simon.
Here is my code for XMLPiece, thanks!
#import "XMLLevelPiece.h"
#import "BlockType.h"
#import "GridCord.h"
#import "BlockColor.h"
#implementation XMLLevelPiece
BlockTypeEnum mBlockType;
BlockColorEnum mBlockColor;
int mRotation;
GridCord* mGridCords;
BlockColorEnum mLeftColor;
BlockColorEnum mTopColor;
BlockColorEnum mRightColor;
Boolean mRotatable;
Boolean mMoveable;
int mGroupID;
-(id) init
{
if( (self=[super init])) {
}
return self;
}
-(void)initPiece:(BlockTypeEnum)pBlockType pBlockColor:(BlockColorEnum)pBlockColor pRotation:(int)pRotation pGridCords:(GridCord*)pGridCords pLeftColor:(BlockColorEnum) pLeftColor pTopColor:(BlockColorEnum) pTopColor pRightColor:(BlockColorEnum) pRightColor pRotatable:(Boolean) pRotatable pMoveable:(Boolean) pMoveable pGroupID:(int) pGroupID
{
mBlockType = pBlockType;
mBlockColor = pBlockColor;
mRotation = pRotation;
mGridCords = pGridCords;
mLeftColor = pLeftColor;
mTopColor = pTopColor;
mRightColor = pRightColor;
mRotatable = pRotatable;
mMoveable = pMoveable;
mGroupID = pGroupID;
}
-(void)initPiece2
{
NSLog(#"poo");
}
-(Boolean)getRotatable
{
return mRotatable;
}
-(Boolean)getMoveable
{
return mMoveable;
}
-(int) getGroupID
{
return mGroupID;
}
-(BlockColorEnum) getLeftColor
{
return mLeftColor;
}
-(BlockColorEnum) getTopColor
{
return mTopColor;
}
-(BlockColorEnum) getRightColor
{
return mRightColor;
}
-(BlockTypeEnum) getBlockType
{
return mBlockType;
}
-(BlockColorEnum) getBlockColor
{
return mBlockColor;
}
-(int) getRotation
{
return mRotation;
}
-(id) getGridCords
{
return mGridCords;
}
-(void) setRotatable:(Boolean) pRotatable
{
mRotatable = pRotatable;
}
-(void) setMoveable:(Boolean) pMoveable
{
mMoveable = pMoveable;
}
#end
TL;DR
The answer is - that is not how you define ivars in Objective-C. I'm not even sure how that is supposed to behave but I can reproduce the error if I code it the same as you have.
I'd be interested for someone with more knowledge to explain what the behaviour/scope of those variable should be when they are defined like you have.
There's a lot of flaws in that code
You have not actually shown an initPiece:.
The long init... with all the arguments is likely a bad idea. Generally only add things to an init as either a convenience or if the object simply cannot function without it.
The use of get is not really correct in Objective-C
The class should potentially be defined more like
XMLLevelPiece.h
// You will need to import the header with the BlockTypeEnum defined
#interface XMLLevelPiece : NSObject
#property (nonatomic, assign) BlockTypeEnum blockType;
// .. Other properties
- (id)initWithPiece:(BlockTypeEnum)blockType; // I'm not so sure you need this
#end
XMLLevelPiece.m
#import "XMLLevelPiece.h"
#import "BlockType.h"
#import "GridCord.h"
#import "BlockColor.h"
#implementation XMLLevelPiece
#synthesize blockType = mBlockType;
- (id)initWithPiece:(BlockTypeEnum)blockType;
{
self = [super init];
if (self) {
mBlockType = blockType;
}
return self;
}
#end
Then you can use it like
BlockTypeEnum blockType = STRAIGHT;
XMLLevelPiece *p1 = [[XMLLevelPiece alloc] initWithPiece:blockType];
blockType = NINETYBEND_FAST;
XMLLevelPiece *p2 = [[XMLLevelPiece alloc] initWithPiece:blockType];
NSLog(#"%d", p1.blockType);
NSLog(#"%d", p2.blockType);
Which for me results in:
2012-01-08 15:29:31.782 Untitled[1297:707] 3
2012-01-08 15:29:31.791 Untitled[1297:707] 1
Optional observations
If you can do away with the dedicated initializer, the usage would look more like:
BlockTypeEnum blockType = STRAIGHT;
XMLLevelPiece *p1 = [[XMLLevelPiece alloc] init];
p1.blockType = blockType;
// all other assignments
blockType = NINETYBEND_FAST;
XMLLevelPiece *p2 = [[XMLLevelPiece alloc] init];
p2.blockType = blockType;
// all other assignments
NSLog(#"%d", p1.blockType);
NSLog(#"%d", p2.blockType);
To remove a couple of superflous lines you could remove the local blockType variable and assign the value straight to the object:
XMLLevelPiece *p1 = [[XMLLevelPiece alloc] init];
p1.blockType = STRAIGHT;
Your method call to initWithPiece does not match the definition.
Call:
[piece initPiece:blockType];
Definition:
-(void)initPiece:(BlockTypeEnum)pBlockType pBlockColor:(BlockColorEnum)pBlockColor pRotation:(int)pRotation pGridCords:(GridCord*)pGridCords pLeftColor:(BlockColorEnum) pLeftColor pTopColor:(BlockColorEnum) pTopColor pRightColor:(BlockColorEnum) pRightColor pRotatable:(Boolean) pRotatable pMoveable:(Boolean) pMoveable pGroupID:(int) pGroupID
Comments:
In general method calls with more than a few parameters are best a poor idea. Probably better in this case is using individual setters.
The convention in Objective-C as per Apple is to name getters without the "get" prefix. In fact a get prefix signifies a value returned via a reference parameter, not as the return result. Such usage will confuse the Analyzer and cause problems if using ARC.