Override a property to make it read-only from the subclass - objective-c

I would like to subclass UILabel in such a way that the user of the class cannot set the text directly through label.text = #"foo". Instead I'd like to set the text from inside the subclass depending on some values.
What I tried:
BalanceLabel.h:
#interface BalanceLabel : UILabel
#property(nonatomic,copy, readonly) NSString *text;
#end
However, I get a warning telling me I'm restricting text access (like I wanted to) but I don't get any compile time errors if I try to set the text directly using an object of my subclass.

You can't do this. As a trivial example as to why not, just think of how the following code should behave:
UILabel *label = [[BalanceLabel alloc] init];
label.text = #"string";
That code creates a BalanceLabel, but stores it in a variable of type UILabel, which means that the subsequent setting of the .text property can't know that you tried to make the property readonly in BalanceLabel.
Unfortunately there's not much you can do about this. You could override the setter to throw an exception, which will let users know what they did wrong, but of course will also crash the app.

You should be putting the logic into controller that managed the view instead of view directly.
I assume you have some view that gets updated with new values and you want to update the BalanceLabel based on these new values.
Your controller is a delegate for your view so it receives new values, from either user or other modules of your app that populated new values (like loaded from file, downloaded from network and so on).
Your controller then figures out which bits of view needs update and sets new values - in your case calculate balance, I assume

Related

Why isn't the name property of my NSImage being copied when I call `-copy`?

I have an NSArray ("objects") that contains objects of type NSImage
I have found that when I try to make a copy of each image in the array, the name property on the NSImage does not get copied.
for (NSObject* object in objects)
{
NSMenuItem* menuItem = nil;
if ([object isKindOfClass:[NSImage class]])
{
NSImage* image = [object copy];
//Breakpoint here
Here is a screenshot of my variable view at the breakpoint above ^^
How could it be that the name property isn't being copied?
Name on images is used as a special registration mechanism so you can look the images up by name later. As such you can only have one image with each name. If you change the name on an image then the original registration is removed and the new one added. If an image already have the name you try to set then the set is declined.
Effectively what you're seeing is an attempted re-registration of an image name against a different image. That image might be a copy, so you might think it's fine to keep the name, but you don't know what the private implementation is and you also aren't considering potential mutable subclasses.
So, you can't share names across multiple different images. If you want to do something similar then you need to manage the names / identifiers separately using a custom scheme / approach.

NSTableView + NSTextView = Disaster :(

This is driving me a bit crazy..
Down below is a screenshot of my program so far.
On the right is an NSTableView (view-based). This is where the user can select a document they want to work on.
On the left is the NSTextView. Text will be displayed depending on what item they choose in the NSTableView.
There are also big plus and minus buttons for creating/deleting new items in the tableview.
Simple right? I wish.
Right now I have it so the tableview gets data from a mutable array. The mutable array contains objects of a class called DocumentItem. The DocumentItem just has two strings, one for the document text and one for the document title.
What works so far:
When I manually add objects to the array using code, I am able to freely switch through the documents and the textview will update accordingly.
What doesn't work:
When the user switches to a different document, I want to call the NSTableView replaceObjectAtIndex method save the changes that they have made to the object in the array.
How my code works so far:
The mutable array is stored in a data class. The data class is a shared class and is referred to in my code as theDATA.
I have a thread looping in my class that has the textview. In my tableview class I have a method called blastToScreen that will change a BOOL called shouldBLAST to YES.
Here is the code in my TableController class to set the BOOL to YES:
- (void) blastToScreen{
theDATA.blasttext = [[theDATA.globaldoclist objectAtIndex:[tablevieww selectedRow]] doccontents];
theDATA.shouldBLAST=YES;
}
Here is the shouldBLAST method in my looped thread(in a different class from the textview). Please note that the if-statement that says if(theDATA.switchedrow) is there to make sure that certain code gets runned only when a user switches their row in the tableview.
if(theDATA.shouldBLAST){
if(theDATA.switchedrow){
DocumentItem * itemr = [theDATA.globaldoclist objectAtIndex:theDATA.lastindex];
NSLog(#"(%li) prev content - >%#",(long)theDATA.lastindex,itemr.doccontents);
itemr.doccontents=textvieww.string;
NSLog(#"(%li)adding content - > %# <- to %#",theDATA.lastindex, itemr.doccontents,itemr.docname);
theDATA.switchedrow=NO;
[theDATA.globaldoclist replaceObjectAtIndex:theDATA.lastindex withObject:itemr ];
NSLog(#"changed: - > %#",[[theDATA.globaldoclist objectAtIndex:theDATA.lastindex] doccontents]);
}
textvieww.string=theDATA.blasttext;
theDATA.shouldBLAST=NO;
NSLog(#"changed: - > %#",[[theDATA.globaldoclist objectAtIndex:theDATA.lastindex] doccontents]);
theDATA.lastindex=theDATA.selectedrow;
}
Here's the weird part about all this:
According to the NSLog statements I set up, my code works for a split second and then resets.
Down below is what the console says. ignore the (0). that is just talking about the last selected index.
What it is saying is that the text before switching was nothing(fine). It is saying that it is adding the text "Potato" to that array(still fine). Then, the first time I fetched the object from the array it shows that it successfully changed to "Potato"(Still fine). Then when I tried to fetch the SAME exact data a few lines later, it returned nothing. :(
I feel like the issue resides somewhere in my TableController class. Here's a link to the code in my TableController class.
Here's what the console returned:
2015-09-14 17:17:46.024 Simplicity[4801:432580] (0) prev content - >
2015-09-14 17:17:46.025 Simplicity[4801:432580] (0)adding content - > Potato <- to Untitled
2015-09-14 17:17:46.025 Simplicity[4801:432580] changed: - > Potato
2015-09-14 17:17:46.025 Simplicity[4801:432580] changed: - >
I really hope you guys can help me. I tried pretty much everything I could to solve this issue.This is holding me back from finishing my software.
Probably, the doccontents property of your DocumentItem class is strong (or retain) when it should be copy.
From the docs for the string property of NSText (from which NSTextView inherits):
For performance reasons, this method returns the current backing store of the text object. If you want to maintain a snapshot of this as you manipulate the text storage, you should make a copy of the appropriate substring.
So, if you're just keeping a reference to that same object, when the text view's content is changed, the content of the object you've got a reference to also changes. You need to make a private copy.

How to name an Instance of an NSObject, from an NSString or UITextField (to add to NSMutableArray)

When I add an instance of my NSObject class, as an additional item, to my NSMutableArray, it adds it as the same name each time. (the #properties inside the NSObject are different).
I want to be able to name the instances (of the NSObject Class) something different, so that when they get added to the NSMutableArray, each time (that the user hits the add button), they are differently named items. How can I name the instances something different, based on an NSString / UITextfield input? Many thanks.
Additional info :
FirstViewCont.h _ #property ABDataNSO *ABDataNSOItem; // Data Storage 'toDoItem'
FirstViewCont.m _ #synthesize ABDataNSOItem;
// In my view controller.m file, I Alloc & Init my instance of 'ABDataNSO' NSObject class, which has some user inputted #properties in it.
ABDataNSOItem = [[ABDataNSO alloc] init];
// Then I add that instance of my NSObject, to my NSMutableArray, when the user hits a button to add/ save their details.
[self.ABMutableTempArray addObject:ABDataNSOItem];
This outputs ..
NSLog(#"sssssssssss%#", self.ABMutableTempArray);
2014-09-06 00:37:15.844 ABAppMini011[2687:60b] All of the items in my
Array are called ABDataNSO. (
"ABDataNSO: 0x8e321e0",
"ABDataNSO: 0x8cc9c00",
"ABDataNSO: 0x8c829f0"
For example, I would rather the items be called “ABDataNSORace1, ABDataNSORace2, ABDataNSORace3.” Any suggestions?
Those strings are devised by way of the NSObject method description. There are a couple ways you could manipulate that:
Override description in your ABDataNSO class so that you can setDescription:(NSString*) or similar per instance.
Either use this setDescription: every time you add to your NSMutableArray, OR
Subclass NSMutableArray for your implementation, and add an addObject: withDescription or something like that, and keep up with the description of each object for use in your own overridden description method. I don't like this approach though.

Accessing property of other class

I've got a UIViewController which has three views: Category -> Sub-Category -> Detail View. The content of the Sub-Category and Detail View depends on which row is clicked in the Category view. This is done with a property called 'categoryClicked' and is declared in the sub-category.m file. It's value is given in the category.m file by the following lines of code.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Sub-category *sub = [[Sub-category alloc] initWithNibName:#"Sub-category" bundle:nil];
sub.categoryClicked = [_categoryArray objectAtIndex:indexPath.row];
[self.navigationController pushViewController:rubrieken animated:YES];
}
This works perfectly fine until I want to use the value of categoryClicked in the Detail View. In both the category.m and the DetailView.m sub-category.m is imported. For testing purposes I putted a label on the Detail View and in the DetailView.m file I've got the following code:
Sub-category *sub = [[Sub-category alloc] initWithNibName:#"Sub-category" bundle:nil];
label.text = sub.categoryClicked;
I'm convinced this code should do the job but in fact I get an empty label. Is there anybody able to tell me what I'm doing wrong.
Edit
categoryClicked is a property declared in Sub-category.h and synthesized in Sub-category.m.
Wanted to post some more code but there is no more relevant code.
This line...
Sub-category *sub = [[Sub-category alloc] initWithNibName:#"Sub-category" bundle:nil];
...creates a new Sub-category. Since it's new, it doesn't know anything about what information has been given to some other Sub-category object. You need to get a reference to the existing object if you want to access its data.
When you alloc an object, you're allocating memory space for it. Then when you use something with init (like initWithNibName) you're initializing it. So when you allocate memory space for sub and initialize it, you have one object. But then you alloc and init again, which creates an entirely new (and completely unrelated) object. Make sure that you remove anything that could destroy your old object.
Also, the * symbol means that sub is a pointer (it points to a memory location). Whenever you use the assignment operator (=) you're telling it to point to a new thing. What you're doing is telling the label.text pointer to point at what sub is pointing at. But if you change what sub is pointing at and point label.text at the same thing, neither one is pointing at the value you want.
Hope this is clear enough, if it's not trying posting some more code and maybe someone can suggest exact changes.
-EDIT-
If you want to have a reference to an object you can only get it a few ways. What's important to know is that you can't really "create" a reference to an existing object. You have to have some connection to the object.
Declare the object inside of the file you want the reference in with something like Category c = [[Category alloc] init]; Remember this creates a new object, it won't create a reference to an existing object. However, creating the object inside of another object means that one "owns" the other and can do whatever it wants with it (obviously including accessing properties and calling methods).
Use a "chain" of objects to get a reference to your object. So if your file owns a file that owns the object you want you can use topfile.otherfile.objectyouwant. The most obvious example of this is getting a reference to an object owned by a subview.
That's about as basic as it gets; just remember that there aren't any "global" objects that you can just call by name. If your problem isn't solved by this, either look at some sample code and try to figure out how references work, or post another question that's more closely related to your problem

Regular class global variable being shared between object instances - help!

I have just started learning objective c having a little Java experience and this site has been really helpful for answering loads of my questions, but I've hit an issue I can't quite fathom. Theres a fair amount to be read on the topic but I can't quite find where I've gone wrong.
I read this:
Objective C does not support class data members (i.e. data shared by all objects of the same class), however, they can be emulated through the use of the static modifier. For example, declaring a static int count variable in the implementation file of the Employee class would result in all Employee objects sharing a single integer variable called count.
But I seem to have ended up with this functionality without trying... and I don't want it!
The scenario is that I have a Category of UIImageView in which I'm declareing a global variable (just below my #implementation line) to count a particular event and have it used in my new methods.
#implementation UIImageView (Draggable)
int touchesCount = 0;
// methods ...
Everything works fine if I have a single UIImageView. However when I create and add a second instance of UIImageView this count variable is being shared between the two giving unexpected behaviour.
Figure it may be relevant to show how I'm creating the objects:
UIImageView *myImageView = [[UIImageView alloc] initWithFrame:myImageRect];
UIImageView *myImageView2 = [[UIImageView alloc] initWithFrame:myImageRect];
// ... (create, manipulate and set 2 images to the 2 views)
[self.view addSubview:myImageView];
[self.view addSubview:myImageView2];
Other than that, any suggestions on where I might be going wrong?
Thanks!
Edit: To illustrate a bit more what I'm doing with the 2 views, self in this case is a ViewController.
What you are looking for are instance variables, but categories can't add those to an interfaceApple:
Note that a category can’t declare additional instance variables for the class; it includes only methods.
You should be using inheritance instead:
#interface MyView : UIImageView {
int touchesCount;
}
// ...
#end