Strong IBOutlet nil in XIB with multiple views - swift

Calling Bundle.main.loadNibNamed to load a .xib file which contains (n) multiple variants of one UI defined with multiple UIViews instantiates n instances of my subclass.
I then apply a filter expression to choose the correct variant with .first(where: { $0.restorationIdentifier == <correct restoration ID>.
In this instance my filter expression correctly returns the 5th UIView inside of my .xib but the #IBOutlets in my custom class are connected to the 1st UIView that was instantiated but which is immediately deprecated by what I assume is ARC.
This leads me to having unexpectedly nil IBOutlets. What can be done to connect the IBOutlets to the correct (5th in this case) UIView returned by Bundle.main.loadNibBaned

The problem is that loadNibNamed is instantiating all your views, and you're only choosing to keep some of them around. In the process, IB outlets are assigned in some order, which most likely doesn't end up with your desired object being assigned to an outlet last.
I don't think a nib file gives you a way to instantiate only some of its multiple top level objects. You need to either split your various views into multiple nibs (and only load the one you need), or switch to using a Storyboard, which does let you instantiate specific objects by their identifier.

Related

Discover all delegates pointing to a controller?

Say you have a class
class Example:UIViewController, UIScrollViewDelegate
{
at any point in the life of the program, there may be (say) four little scroll views which have embedded themselves in an instance of Example, so those scroll views have said
aScrollView.delegate = someExample
bScrollView.delegate = someExample
and so on.
Is that information, indeed stored somehow, with someExample?
Obviously that information is stored with aScrollView - but is it also stored with someExample? Is the delegate connection two-way or strictly one-way? Can someExample examine itself and determine all the scrollviews which have been delegate'd to someExample?
So, is there a way for someExample to list all the scroll views currently connected to it in that way??
The delegate property is just a (usually weak) reference in the object that holds it. For an object to keep back pointers to its "delegations", it would need to implement its own structure and do the bookkeeping for adding and removing itself.

objective-c: multiple class defintions in one .m file, and calling methods

I've defined two classes in an m file, the first subclassing UIView and the second UIViewController. The UIViewController is instantiated at some point, and the vc is who instantiates my first class.
the first class implements the touchesEnded method, to simulate a button. when the touchesEnded method is fired in the first class, is it possible to easily call a method defined in the 2nd class, without going into delegates and such?
I tried playing with selectors with no luck
is it possible to easily call a method defined in the 2nd class
Yes, assuming that you are creating an instance of the second class and calling the method on that instance.
Regardless of whether the two classes are subclasses of the same type, or in the same or different files, you need a reference to an instance of that class to call a method on it, or force it to perform a selector.
The proper OO way to do this is with delegates, but you could theoretically do something like pass a reference to view 2 into view 1 when you create the views. If you create them in IB you could create outlets so they reference each other that way.
In short: Yes, it is possible and easy to do, but I can't give you too much in terms of specific code without a more specific example of your situation

is it necessary to have instance variables in a UITableViewCell subclass?

Question - Is it necessary to have subview (e.g. UILabel) instance variables in a UITableViewCell subclass?
The alternative I am thinking of being to construct say the UILabels you want for your custom UITableViewCell subclass when you create it, assign them to the content view (e.g. [self.contentView addSubview:label_1]), and then release the UILabel (e.g. [label_1 release]).
So is it the case the only reason you need to keep the labels as instance variables (declared in the header) of the UITableViewCell subclass, so that you can grab them more easily to configure/make changes to them later. That is as opposed to having to find through by looking them up directly in the contentView via their tag values?
thanks
it is a convenience, but one worth sticking with. if you call viewWithTag everytime something needs to be changed/redrawn it can be less efficient than just using the pointer stored in the stack, as this would have to be recalculated each time.

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.

Setting ivar in objective-c from child view in the iPhone

Maybe a FAQ at this website.
I have a TableViewController that holds a form. In that form I have two fields (each in it's own cell): one to select who paid (single selection), and another to select people expense is paid for (multiple selection).
Both fields open a new TableViewController included in an UINavigationController.
Single select field (Paid By) holds an object Membership
Multiple select field (Paid For) holds an object NSMutableArray
Both vars are being sent to the new controller identically the same way:
mySingleSelectController.crSelectedMember = self.crPaidByMember;
myMultipleSelectController.crSelectedMembers = self.crSelectedMembers;
From Paid for controller I use didSelectAtIndexPath method to set a mutable array of Memberships for whom is paid:
if ([[tableView cellForRowAtIndexPath:indexPath] accessoryType] == UITableViewCellAccessoryCheckmark) {
[self.crSelectedMembers removeObject:[self.crGroupMembers objectAtIndex:indexPath.row]];
//...
}
else {
[self.crSelectedMembers addObject:[self.crGroupMembers objectAtIndex:indexPath.row]];
//...
}
So far everything goes well. An mutable array (crSelectedMembers) is perfectly set from child view.
But...
I have trouble setting Membership object.
From Paid By controller I use didSelectAtIndexPath to set Membership:
[self setCrSelectedMember:[crGroupMembers objectAtIndex:indexPath.row]];
By NSlogging crSelectedMember I get the right selected member in self, but in parent view, to which ivar is pointed, nothing is changed.
Am I doing something wrong? Cause I CAN call the method of crSelectedMembers, but I can't change the value of crSelectedMember.
If I understand your question, the most likely cause is an improper property declaration.
If you want to pass values from one object to another using each objects properties, then you need to make sure to use assign to ensure the properties in one object are pointing at the same instances as the property in the other object.
So in your topViewController you have a property:
#property (nonatomic,retain) NSString crSelectedMember;
Then in your child view controllers you have:
#property (nonatomic,assign) NSString crSelectedMember;
This forces the value into the exact object in the parent controller.
However, this is a very fragile way to pass data between viewControllers. As your app becomes more complicated, it will be impossible to track all the passed data. (Worse, if you run into memory limitations, the parent view controller may unload and not even exist when you try to pass data to it.)
Instead, you should have a single custom object devoted to holding your data. Each view controller should query that object for the data it needs and should write any changes back to that object. The view controllers never communicate directly. This technique allows you to control the data in one specific location instead of spreading it out all over your code and it scales well. You can add an arbitrary number of view controllers to you app without having to worry about tying them all together.
See this post for details: iPhone: How to Pass Data Between Several Viewcontrollers in a Tabbar App