iOS Copying a custom object - ios

I load a custom object using :
NSArray* array = [[NSBundle mainBundle] loadNibNamed:#"MyViewItem" owner:nil options:nil];
MyViewItem* myBaseItem = [array objectAtIndex:0];
In order not to perform the loadNibNamed action like a hundred times in the several loops I have to do, I plan to use this object as a "base" object that i'll fill with differents informations, but always based on the same design (like in an UICollectionView actually).
My first move was to set in my loop something like :
for (int i = 0; i < x; i++ ) {
MyViewItem* newItem = [myBaseItem copy];
}
Unfortunately, MyViewItem class tried to accessed "copyWithZone", which i didn't implement. On this point, i tried to inherits NSObject on it (I think all NSObject implements "copy" method), but the issue remains with the same error.
So I tried to inherits NSCopying, and to implement my copyWithZone method, which is the following (not working though) :
#interface MyViewItem : UIView <NSCopying>
// some stuff
#end
#implementation MyViewItem
// some stuff
- (id)copyWithZone:(NSZone *)zone
{
MyViewItem* copy = [[MyViewItem allocWithZone:zone] init];
// What should i do here ?
return copy;
}
#end
What am I missing ? I've read many issues concerning copy, but I couldn't solve mine :(
Thanks !

Related

How add NSDictionary to EKEvent

i would like to add extra data to EKEvent, i tried NSDictionary (there is a lot of data to add) but it doesn't work..
sample code:
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
[eventStore setValue:dictionary forKey:MAIN_DICTIONARY];
any ideas?
You're using setValue:forKey: in a wrong way. That a look here. There are different options to achieve what you want: category, subclassing or create a class that contains the EKEvent and the NSMutableDictionary. It depends on how you need to use the EKEvent.
You cannot do it this way, because even with key-value coding you can only set (declared or non declared) properties known by the instance. Basically the accessors (setter, getter) are executed. But there is no property MAIN_THREAD,no setter setMAIN_THREAD: in EKEvent.
If you want to extend instances of a foreign class that are created by the system (the instances, not the class), there are to ways to add data:
You create an own class, let's say MyEvent and give them a reference to the system instance (EKEvent) as a property plus the properties you need. When you get an instance of EKEvent you look-up your list of MyEventss using the identifier. With that you have the full access to your data.
You use associated objects. But you have to take care that they are not handled by the instance, i. e. while copying.
The first solution is better by far. Simple sample code:
#interface MyEvent : NSObject
#property (readonly) EKEvent* systemEvent;
#property id customProperty;
- (instancetype)eventForSystemEvent:(EKEvent*)systemEvent;
#end
#implemenation MyEvent
// Look-Up
NSMutableDictionary *eventLookUp;
+ (void)initialize
{
if( self == [MyEvent class])
{
eventLookUp = [NSMutableDictionary new];
}
}
- (instancetype)eventForSystemEvent:(EKEvent*)systemEvent
{
return eventLookUp[systemEvent.calendarItemIdentifier];
}
// Instance creation
- (instancetype)initWithSystemEvent:(EKEvent*)systemEvent
{
// Usual initializer
…
eventLookUp[systemEvent.calendarItemIdentifier] = systemEvent;
return self;
}
+ (instancetype)newEventWithSystemEvent:(EKEvent*)systemEvent
{
return [[self alloc] initWithSystemEvent:systemEvent];
}
#end
Typped in Safari

Accessing Variables in another class and dividing NSString

Hy I have a problem accessing variables in another class.
Im making an app that makes note and let you study from the notes you make, for example the user makes a note that says "Oceanic dolphins: are members of the cetacean...", and when the user press a button to study it appears something like this "what are Oceanic Dolphins" then the user press a button it appears something like this "they are members of the cetacean..." the problem I have is this When i enter the ViewController that makes the question it appears empty I think the problem lies on one of the next codes
I make the Variable Globals like this
QueRes.h
#import <Foundation/Foundation.h>
#interface QueRes : NSObject
#property NSString *question;
#property NSString *response;
#end
QueRes.m
#import "QueRes.h"
#implementation QueRes
#end
I divide the NSString of the note like this
NSArray *card = [_argumentTextView.text componentsSeparatedByString:#":"];
QueRes *make = [[QueRes alloc] init];
if ([card count] >= 2)
{
make.question = card [0];
make.response = card [1];
}
the I apply the variable question and response in a ViewController like this
- (void)viewDidLoad
{
[super viewDidLoad];
QueRes *make = [[QueRes alloc] init];
_questionTextView.text = make.question;
}
then in other view controller i have the same code but apply with the response variable
Please help me I been stuck in this for weeks (I have Xcode 5 and the app runs in IOS 7)
(if you need more of the code of the program to help me fix it just tell me )
You are making a new instance of a QueRes in your viewDidLoad method. Unless the init method of QueRes sets its question and response properties to something, they will be uninitialized, which is why you are not seeing anything in your text view: there is nothing to show.
Naming the QueRes instance you make in the third code block you posted make does not make it the same instance as the instance in the viewDidLoad method, and it is not a global variable at all. It is a separate instance of QueRes.

Why is dealloc called immediately after the instantiation?

I have a small problem with ARC and dealloc of the BaseViewController class being called after the instantiation inside the loop and I don't know why. What I'm trying to do is basically store all the base view controllers on an array.
#interface CategoriesContainerViewController ()
#property (nonatomic, strong) IBOutlet UIScrollView* scrollView;
#property (nonatomic, strong) NSMutableArray* categoriesViews;
#end
- (void)viewDidLoad {
[super viewDidLoad];
// Get the categories from a plist
NSString* path = [[NSBundle mainBundle] pathForResource:#"categories" ofType:#"plist"];
NSDictionary* dict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSMutableArray* categories = [dict objectForKey:#"Categories"];
NSLog(#"%i", [categories count]);
// Setup the scrollview
_scrollView.delegate = self;
_scrollView.directionalLockEnabled = YES;
_scrollView.alwaysBounceVertical = YES;
_scrollView.scrollEnabled = YES;
CGRect screenRect = [[UIScreen mainScreen] bounds];
// Loop through the categories and create a BaseViewController for each one and
// store it in an array
for (int i = 0; i < [categories count]; i++) {
BaseViewController* categoryView = [[BaseViewController alloc]
initWithCategory:[categories objectAtIndex:i]];
CGRect frame = categoryView.view.frame;
frame.origin.y = screenRect.size.height * i;
categoryView.view.frame = frame;
[_scrollView addSubview:categoryView.view];
[_categoriesViews addObject:categoryView];
}
}
You are committing a common beginner mistake by keeping a reference to a view controller's view, but not the view controller itself.
You create a BaseViewController object in a local variable categoryView. That's a strong reference, so the object is kept around. Then the loop repeats, and you create a new BaseViewController, replacing the old value in categoryView. When you do that, there are no longer any strong references to the previous BaseViewController that was in categoryView, so it gets deallocated.
If you want the BaseViewController to stick around, you need to keep a strong reference to it somewhere.
In addition to that, you are breaking another rule of iOS development. You should never put one view controller's view(s) inside another view controller's unless you use the parent/child view controller support that was added in iOS 5 and extended in iOS 6. The docs say do NOT do that.
Mixing views from multiple view controllers on the screen will cause you no end of problems. There is tons of housekeeping you have to do in order to make it work, and not all of that housekeeping is documented. Its possible, but it will take you many weeks to iron out the bugs, if you ever able to. Plus, since you are doing something that Apple expressly says not to do, the burden is on you to make it work correctly, and there is a substantial risk that a new iOS release will break your app.
Initialize BaseViewController above for loop and then store the array value inside the object of BaseViewController. Because every time it is allocating and initializing. So setting the previous object to nil. Hence the issue causes to be deallocated.

iOS loadNibNamed confusion, what is best practice?

I'm familiar with most of the process of creating an XIB for my own UIView subclass, but not everything is working properly for me - it's mostly to do with the IBOutlets linking up. I can get them to work in what seems like a roundabout way.
My setup is this:
I have MyClass.h and MyClass.m. They have IBOutlets for a UIView (called view) and a UILabel (called myLabel). I added the 'view' property because some examples online seemed to suggest that you need this, and it actually solved an issue where I was getting a crash because it couldn't find the view property, I guess not even in the UIView parent class.
I have an XIB file called MyClass.xib, and its File's Owner custom class is MyClass, which prefilled correctly after my .h and .m for that class existed.
My init method is where I'm having issues.
I tried to use the NSBundle mainBundle's 'loadNibNamed' method and set the owner to 'self', hoping that I'd be creating an instance of the view and it'd automatically get its outlets matched to the ones in my class (I know how to do this and I'm careful with it). I then thought I'd want to make 'self' equal to the subview at index 0 in that nib, rather than doing
self = [super init];
or anything like that.
I sense that I'm doing things wrong here, but examples online have had similar things going on in the init method, but they assign that subview 0 to the view property and add it as a child - but is that not then a total of two MyClass instances? One essentially unlinked to IBOutlets, containing the child MyClass instantiated via loadNibNamed? Or at best, is it not a MyClass instance with an extra intermediary UIView containing all the IBOutlets I originally wanted as direct children of MyClass? That poses a slight annoyance when it comes to doing things like instanceOfMyClass.frame.size.width, as it returns 0, when the child UIView that's been introduced returns the real frame size I was looking for.
Is the thing I'm doing wrong that I'm messing with loadNibNamed inside an init method? Should I be doing something more like this?
MyClass *instance = [[MyClass alloc] init];
[[NSBundle mainBundle] loadNibNamed:#"MyClass" owner:instance options:nil];
Or like this?
MyClass *instance = [[[NSBundle mainBundle] loadNibNamed:#"MyClass" owner:nil options:nil] objectAtIndex:0];
Thanks in advance for any assitance.
The second option is the correct one. The most defensive code you could do is like this:
+ (id)loadNibNamed:(NSString *)nibName ofClass:(Class)objClass {
if (nibName && objClass) {
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:nibName
owner:nil
options:nil];
for (id currentObject in objects ){
if ([currentObject isKindOfClass:objClass])
return currentObject;
}
}
return nil;
}
And call like this:
MyClass *myClassInstance = [Utility loadNibNamed:#"the_nib_name"
ofClass:[MyClass class]];
// In my case, the code is in a Utility class, you should
// put it wherever it fits best
I'm assuming your MyClass is a subclass of UIView? If that's the case, then you need to make sure that the UIView of your .xib is actually of MyClass class. That is defined on the third Tab on the right-part in the interface builder, after you select the view
All you need to do is create the subview via loadNibNamed, set the frame, and add it to the subview. For example, I'm adding three subviews using my MyView class, which is a UIView subclass whose interface is defined in a NIB, MyView.xib:
So, I define initWithFrame for my UIView subclass:
- (id)initWithFrame:(CGRect)frame
{
NSLog(#"%s", __FUNCTION__);
self = [super initWithFrame:frame];
if (self)
{
NSArray *nibContents =
[[NSBundle mainBundle] loadNibNamed:#"MyView"
owner:self
options:nil];
[self addSubview:nibContents[0]];
}
return self;
}
So, for example, in my UIViewController, I can load a couple of these subclassed UIView objects like so:
for (NSInteger i = 0; i < 3; i++)
{
CGRect frame = CGRectMake(0.0, i * 100.0 + 75.0, 320.0, 100.0);
MyView *myView = [[MyView alloc] initWithFrame:frame];
[self.view addSubview:myView];
// if you want, do something with it:
// Here I'm initializing a text field and label
myView.textField.text = [NSString stringWithFormat:#"MyView textfield #%d",
i + 1];
myView.label.text = [NSString stringWithFormat:#"MyView label #%d",
i + 1];
}
I originally advised the use controllers, and I'll keep that answer below for historical reference.
Original answer:
I don't see any references to view controllers here. Usually you'd have a subclass of UIViewController, which you would then instantiate with
MyClassViewController *controller =
[[MyClassViewController alloc] initWithNibName:#"MyClass"
bundle:nil];
// then you can do stuff like
//
// [self presentViewController:controller animated:YES completion:nil];
The NIB file, MyClass.xib, could specify that the base class for the UIView, if you want, where you have all of the view related code (e.g. assuming that MyClass was a subclass of UIView).
Here's one method that I use:
Create a subclass for UIView, this will be called MyClass
Create a view xib file. Open in interface builder,
click File's Owner and in the Identity Inspector, change the class
to that of your parent view controller, e.g.
ParentViewController.
Click the view already in the list of objects and change it's
class in Identity Inspector to MyClass.
Any outlets/actions that you declare in MyClass will be
connected by click-dragging from View (not File's Owner). If you want to connect them to variables from ParentViewController then click-drag from File's Owner.
Now in your ParentViewController you need to declare an instance
variable for MyClass.
ParentViewController.h add the following:
#class MyClass
#interface ParentViewController : UIViewController {
MyClass *myClass;
}
#property (strong, nonatomic) MyClass *myClass;
Synthesize this in your implementation and add the following in your
viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSBundle mainBundle] loadNibNamed:#"MyClass" owner:self options:nil];
self.myClass.frame = CGRectMake(X,Y,W,H); //put your values in.
[self.view addSubview:self.myClass];
}

NSDictionary: method only defined for abstract class. My app crashed

My app crashed after I called addImageToQueue. I added initWithObjects: forKeys: count: but it doesn't helped me.
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[NSDictionary initWithObjects:forKeys:count:]:
method only defined for abstract class.
Define -[DictionaryWithTag initWithObjects:forKeys:count:]!'
my code
- (void)addImageToQueue:(NSDictionary *)dict
{
DictionaryWithTag *dictTag = [DictionaryWithTag dictionaryWithDictionary:dict];
}
#interface DictionaryWithTag : NSDictionary
#property (nonatomic, assign) int tag;
- (id)initWithObjects:(id *)objects forKeys:(id *)keys count:(NSUInteger)count;
#end
#implementation DictionaryWithTag
#synthesize tag;
- (id)initWithObjects:(id *)objects forKeys:(id *)keys count:(NSUInteger)count
{
return [super initWithObjects:objects forKeys:keys count:count];
}
#end
Are you subclassing NSDictionary? That's not a common thing to do in Cocoa-land, which might explain why you're not seeing the results you expect.
NSDictionary is a class cluster. That means that you never actually work with an instance of NSDictionary, but rather with one of its private subclasses. See Apple's description of a class cluster here. From that doc:
You create and interact with instances of the cluster just as you would any other class. Behind the scenes, though, when you create an instance of the public class, the class returns an object of the appropriate subclass based on the creation method that you invoke. (You don’t, and can’t, choose the actual class of the instance.)
What your error message is telling you is that if you want to subclass NSDictionary, you have to implement your own backend storage for it (for example by writing a hash table in C). It's not just asking you to declare that method, it's asking you to write it from scratch, handling the storage yourself. That's because subclassing a class cluster directly like that is the same as saying you want to provide a new implementation for how dictionaries work. As I'm sure you can tell, that's a significant task.
Assuming you definitely want to subclass NSDictionary, your best bet is to write your subclass to contain a normal NSMutableDictionary as a property, and use that to handle your storage. This tutorial shows you one way to do that. That's not actually that hard, you just need to pass the required methods through to your dictionary property.
You could also try using associative references, which "simulate the addition of object instance variables to an existing class". That way you could associate an NSNumber with your existing dictionary to represent the tag, and no subclassing is needed.
Of course, you could also just have tag as a key in the dictionary, and store the value inside it like any other dictionary key.
From https://stackoverflow.com/a/1191351/467588, this is what I did to make a subclass of NSDictionary works. I just declare an NSDictionary as an instance variable of my class and add some more required methods. It's called "Composite Object" - thanks #mahboudz.
#interface MyCustomNSDictionary : NSDictionary {
NSDictionary *_dict;
}
#end
#implementation MyCustomNSDictionary
- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(NSUInteger)cnt {
_dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:cnt];
return self;
}
- (NSUInteger)count {
return [_dict count];
}
- (id)objectForKey:(id)aKey {
return [_dict objectForKey:aKey];
}
- (NSEnumerator *)keyEnumerator {
return [_dict keyEnumerator];
}
#end
I just did a little trick.
I'm not sure that its the best solution (or even it is good to do it).
#interface MyDictionary : NSDictionary
#end
#implementation MyDictionary
+ (id) allocMyDictionary
{
return [[self alloc] init];
}
- (id) init
{
self = (MyDictionary *)[[NSDictionary alloc] init];
return self;
}
#end
This worked fine for me.

Resources