How to programmatically assign/replace an existing UITableView with a new one? - iphone

I have the following code to my UIViewController class implementation:
- (void)viewDidLoad {
CustomUITableView *customUITableView = [[CustomUITableView alloc] initWithFrame:self.view.frame];
[self.view addSubview:customUITableView];
[super viewDidLoad];
}
When I run the app, the table shows up just fine. There are two problems I have with this approach:
I lose the use of StoryBoard since the table is dynamically added to the view. I would like to arrange my view in StoryBoard and not have to manually tweak its position in code.
I also want the ability to swap between CustomUITableViews at runtime (if possible). For example, if I create a "CustomUITableView_Number2", can I simply decide which custom class I want to use for the table at runtime?
I know I can accomplish this in StoryBoard by assigning a custom class to the UITableView, but I want to see how far I can go using only code. My goal is to arrange the layout using StoryBoard, but assign the custom class I want at runtime.
Does anyone know if this is possible? Sample code would really help. Thank-you.

You could load a regular UITableView from interface builder and then initialize a CustomUITableView with the parameters you are interested in from the UITableView.
- (void)viewDidLoad {
//Your tableView was already loaded
CustomUITableView *customUITableView = [[CustomUITableView alloc] initWithFrame:tableView.frame style:tableView.style];
customUITableView.backgroundColor = tableView.backgroundColor;
//...
//... And any other properties you are interested in keeping
//...
[self.view addSubview:customUITableView];
[super viewDidLoad];
}
This approach will work if you make sure to specify each property you think you would modify in interface builder. Just make sure to remove the UITableView that was loaded via the nib after you copy the properties over.

sure you can replace the table.
something as simple as
Class MyTableClass = NSClassFromString(#"WhateverClass");
id newTableView = [[MyTableClass alloc] init];
[newTableView performSelector:#selector(someSel:)];
make sure you set your datasource and delegate and whatever properties from the original nib instantiated view first, then
you could then set your local tableview property to this new object and as long as the ivar has a proper cast you could call methods on it without resorting to runtime calls like above.
You can also dig into the obj-c runtime if you wanted to do it in a different way.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html

Related

Dynamically loading NIBs?

Rather than having three separate controllers and their associated *.xib files I am trying to setup a generic controller and then instantiate it with one of three different xib files RED.xib" "GREEN.xib" & "BLUE.xib"
NSString *nibColor;
switch (selectedRow) {
case 0:
nibColor = #"RED";
break;
case 1:
nibColor = #"GREEN";
break;
case 2:
nibColor = #"BLUE";
break;
}
ColorController *colorController = [[ColorController alloc] initWithNibName:nibColor bundle:nil];
My problem is that I am not linking the view and get the following error.
loaded the "RED" nib but the view outlet was not set.
I understand that normally you link the view in IB, but is there a way to dynamically pick the nib at runtime, or do I need to create separate redController, blueController and greenControllers?
cheers Gary
From Apple's UIViewController docs, which I'm assuming ColorController is a subclass of:
When you define a new subclass of UIViewController, you must specify the views to be managed by the controller. There are two mutually exclusive ways to specify these views: manually or using a nib file. If you specify the views manually, you must implement the loadView method and use it to assign a root view object to the view property. If you specify views using a nib file, you must not override loadView but should instead create a nib file in Interface Builder and then initialize your view controller object using the initWithNibName:bundle: method. Creating views using a nib file is often simpler because you can use the Interface Builder application to create and configure your views graphically (as opposed to programmatically). Both techniques have the same end result, however, which is to create the appropriate set of views and expose them through the view property.

UITableViewCell doesn't clear context before drawing

I have a subclass of UITableViewCell which contains several elements - UIImageViews, Labels, etc.
Since this cell is intended to be reusable, I want to be able to change it's appearance a bit depending on what data it is currently displaying.
So as an example - I have this view in my custom UITableViewCell:
UIImageView* delimeterView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cellDelimiter.png"]];
Which I want to be able to hide sometimes like this:
- (void) setRecord:(id)record__ {
if (record__.type == NO_DELIMETER_VIEW)
delimeterView.hidden = YES;
else
delimeterView.hidden = NO;
[self setNeedsLayout];
}
But the problem is that delimeterView will always be displayed on the cell, just like if it was drawn once in the init method and then drawing context was never changed or cleared. I've tried setting clearsContextBeforeDrawing property to YES for both cell and its contentView, I've also tried setting opaque for cell and its contentView to NO since I've read there might be some problems with that aswell in case you're using transparent views.
Nothing helps.
It looks like UITableViewCell never clears its graphic context and just paints over old elements.
Any tips on what am I doing wrong?
I know I can probably fix this by doing custom drawing but I'd rather not.
First, are you sure that delimeterView in setRecord: is actually pointing to your delimeterView? In the code example you give, you assign it to a local. Do you later assign this to an ivar? (You should always use accessors to access ivars: self.delimeterView).
Next, calling -setNeedsLayout just schedules a call to -layoutIfNeeded, which walks the hierarchy calling -layoutSubviews. The default implementation of -layoutSubviews does nothing. You probably meant to call -setNeedsDisplay here, or you need to implement -layoutSubviews to do what you want.

Adding item to a subview from a external class

I've created a UIView and can add things to it by using [self.topView addSubview:image]; Now I am importing a class to create a calendar which has a heap of buttons. I could put it in the same class and say [self.topView addSubview:button] but if its in another class how do I add it to the subview of the class that owns it? Hope that makes sense...
You need a reference in your external class to the class that owns the view (call it the "owner class"), and presumably write a method in your owner class to add a passed-in view to your chosen subview. Something along the lines of:
- (void) insertSubview:(UIView*)newView {
if (newView) [self.topView addSubview:newView];
}
Setting up the reference can be done a number of ways, so I'll leave that one to you.

Count method of subclass of NSMutableArray crashes app

This seems to be a common problem, but I can't figure out anything from the answers I've seen so far. I have an iPhone app that uses a subclass of NSMutableArray to store objects, plus some additional properties. The subclass is skhCustomArray. The subclass initializes fine, with no objects in the skhCustomArray, and I assign it to the the property of my view controller, which is a pointer to an skhCustomArray.
prescriptionListVC* newPrescList = [[prescriptionListVC alloc] initWithNibName:#"PrescriptionList" bundle:nil];
newPrescList.curPersonPrescriptions = [personDetails objectAtIndex:0];
That works fine. Yet when I push my view managed by my view controller onto the navigation controller stack, the count method in the numberOfRowsInSection method crashes the app, see below.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [curPersonPrescriptions count];
}
What could be causing this? How can a valid custom array, with no objects, not return a valid count? Where am I going wrong? Thanks.
Subclass of NSArray? You're aware that NSArray is a class cluster, and is therefore somewhat difficult to subclass, right? In fact, it's so fraught with danger that the NSArray documentation has a whole section dedicated to what you need to do in order to subclass it.
I'll bet that that's the source of your woes.
You almost certainly don't need to subclass NSMutableArray in this situation. Instead, make a new class which has an array as a property, along with the extra properties you desire.
When you subclass NSMutableArray, you need to implement some mandatory methods like count, addObject:, insertObjectAtIndex etc. This is what we call as class cluster.
If you want to add some more feature/behavior to already implemented object then you can write a "category" instead of "subclassing" it.
If you want to subclass it, then you have to implement all those methods which your are going to use so better write a category of NSMutableArray and extend the feature what you want and use the NSMutableArray object only. This will solve your problem and also this is the easy and almost right way to add new behavior to already existing class.

How to use NSLocalizedString in IB [iPhone SDK]?

When I assign text programmatically, I can use NSLocalizedString(....), but if I am writing the text directly in IB (say to a UILabel) how do I localize it?
One solution is to create multiple localized nib files. This probably works best if your localization needs are very straightforward and you're simply going hand your resources over to expert software localizers at the end of development.
If you're localizing while you're developing and the UI is changing rapidly, duplicate nib files can be a big pain, since every UI tweek must be duplicated in each local version of the nib. To avoid this, you'll need to write some code in your view controllers to handle setting the localized strings, typically in the view controller's -viewDidLoad method. Make sure that every control with localized text is an IBOutlet and wired them up to your view controller in IB. Then your view controller's -viewDidLoad will look something like this:
- (void)viewDidLoad {
[super viewDidLoad];
hello.text = NSLocalizedString(#"Hello", #"Hello label");
world.text = NSLocalizedString(#"world", #"world label");
// ... etc
}
You do this type of view setup in -viewDidLoad since the objects in your nib aren't fully created until after your -init method runs. -viewDidLoad runs after -init but before your view becomes visible.
This is a large and complex topic. A good starting place is Introduction to Internationalization Programming Topics and there is also Preparing Your Nib Files for Localization
You can create multiple versions of a .nib file for each locale. There are also tools to let you edit the strings in them easily. Apple has pretty good documentation on this.
I also was searching for a solution, not necessarily for iPhone, but XCode/IB in general. All references did not deal with the fact that you might need a key internally to indicate a state and want to display a localized string for the user in a label or text cell corresponding to that key. I did not found in first step a standard approach how to store e.g. a key value for a key in shared user defaults and display the localized string for that key value in a label.
I found a solution wich does not need many coding and is compliant with the bindings in ib.
First you provide a file Localizable.strings e.g. with a line containing
"MyKeyValue" = "Localized display label";
Now you can localize the key value with: NSLocalizedString(aKeyValue,nil).
In the label you did not find any value transformer dealing with NSLocalized String.
So I created a class KeyToLocalizedStringTransformer to transform a key value into a localized string:
#interface KeyToLocalizedStringTransformer : NSValueTransformer {}
#implementation KeyToLocalizedStringTransformer
+ (Class)transformedValueClass
{
return [NSString class];
}
+ (BOOL)allowsReverseTransformation
{
return NO;
}
- (id)transformedValue:(id)aValue
{
NSString *NLString = [NSString stringWithString:NSLocalizedString(aValue,nil)];
return NLString;
}
Last step for preparing is to register the transformer e.g. in +initialize:
NSValueTransformer *transformer = [[KeyToLocalizedStringTransformer alloc] init];
[NSValueTransformer setValueTransformer:transformer forName:#"KeyToLocalizedStringTransformer"];
Now you can use a value transformer in the bindings for the text field or cell (Simply type in the name if you do see only the NSUnArchiveFromData and so on...)
Sorry no image here from IB 'cause I am new here and "have no reputation": you have to imagine the binding to the shared user defaults controller, Kontroller key: values, Model Key Path: MyStateKey and a value transformer as described.
As a result you dont have to do anything in the nl duplicated nib with the label, simply translate the string in the Localizable.strings.
from iOS 7 & Xcode 5 on, you should avoid using NSLocalizedString where possible. The preferred method is called 'base localisation', and works through the Storyboard. It will save you a lot of work. If you google for 'base localisation' you'll find enough tutorials to get you going