Query a nib file for frames, layout data - ios

I have a UIView subclass that instantiates itself from a nib file. Prior to that, I would like the superview to be able to ask the custom view what frame it's going to need/want.
Currently I have a class method on the custom view that I update by hand when the frame of the custom view in the nib is changed in IB. Ideally I would like a class method that peeks in the nib and reports back directly from the nib.
How can I peek into a nib file and retrieve information from it? Should I load it as NSData and parse the XML or is there something a little less brutal?

If an instance method will work, you should be able to use the -sizeThatFits: method.
If it definitely needs to be a class method, you can write your own class method that is very similar to -sizeThatFits: where you take the CGSize param that is passed in and calculate the appropriate size for the view.
You're not specifically getting data from the nib, you're getting data from the associated class.

Related

How to properly subclass an existing row

I'm needing help in correctly subclassing an existing row so I can change the UI only, not the function. The type of row I'd like to subclass is TextRow.
Again, I only want to change the UI and not the functionality.
Eurek mentions here a way to do it but I can't seem to figure out the correct way to do it. I'd like to form rows look like this.
The steps according to Eureka:
Subclassing cells using the same row
Sometimes we want to change the UI look of one of our rows but without changing the row type and all the logic associated to one row. There is currently one way to do this if you are using cells that are instantiated from nib files. Currently, none of Eureka's core rows are instantiated from nib files but some of the custom rows in EurekaCommunity are, in particular the PostalAddressRow which was moved there.
What you have to do is:
Create a nib file containing the cell you want to create.
Then set the class of the cell to be the existing cell you want to modify (if you want to change something more apart from pure UI then you should subclass that cell). Make sure the module of that class is correctly set
Connect the outlets to your class
Tell your row to use the new nib file. This is done by setting the cellProvider variable to use this nib. You should do this in the initialiser, either in each concrete instantiation or using the defaultRowInitializer. For example:
<<< PostalAddressRow() {
$0.cellProvider = CellProvider(nibName: "CustomNib", bundle: Bundle.main)
}
You could also create a new row for this. In that case try to inherit from the same superclass as the row you want to change to inherit its logic.
There are some things to consider when you do this:
If you want to see an example have a look at the PostalAddressRow or the CreditCardRow which have use a custom nib file in their examples.
If you get an error saying Unknown class <YOUR_CLASS_NAME> in Interface Builder file, it might be that you have to instantiate that new type somewhere in your code to load it in the runtime. Calling let t = YourClass.self helped in my case.
Basically what I need help with is setting up the correct xib/nib and class to use within the form. Thank you.

What exactly does a nib file look like if you were to look inside?

I'm just learning about nibs and swift and am curious about something. I know that if you have a main.storyboard file, that a nib is loaded first fir the root view controller, then for any views that may exist hierarchically under that view controller, however... I'm wondering something.
When they say a nib is 'loaded' does that mean that, a single function CALLS instances of the ui objects that you would like to load, based on the storyboard that you have built in the interface builder?
If not, what is the right way of looking at this? I'm trying to collect an easy to understand, accurate mental model of what happens.
Thanks!
What exactly does a nib file look like if you were to look inside?
Go right ahead and look inside and see! It's just an XML file; perfectly readable by a human.
I know that if you have a main.storyboard file, that a nib is loaded first fir the root view controller, then for any views that may exist hierarchically under that view controller
Correct! To be more precise, think of every "scene" in the storyboard as comprising two nibs, one containing the view controller, the other containing its view, the view's subviews, and everything else constituting that scene.
When they say a nib is 'loaded' does that mean that, a single function CALLS instances of the ui objects that you would like to load, based on the storyboard that you have built in the interface builder?
Think a nib as just a bunch of potential object instances. Loading the nib turns those potential objects into actual instances. Loading a nib is thus just another way of instantiating and configuring objects. A nib being "loaded" simply means that the nib is read and the objects it describes are instantiated and retrieved. That is why you can load the same nib multiple times to get multiple instances of those objects.

I want to get the height from .xib file that I have created for customs cells

I have 5 different types of custom cells. So I want to get different heights from the custom cells because I'm using these cells number of times based on my requirement. Is there any way to write a code which will calculate the height of .xib and will set the height of cell as the .xib height. And the height of cell should change based on the custom cell that I have choosen.
My question is similar to this question but I'm not getting the solution as they said here:
Change tableview row height based on multiple cell xib
If you are implementing the custom table cell from xib, You need to subclass the tablecell method. You can use awakeFromNib to customize your table cell!!!
To know the cell height, you need to over ride the layout subviews method, and you can get the cell height by the following code:
self.frame.size.height
You can add an observer to the frame in the layout subviews method, so that you can get a notification, whenever there is a change in the height!!!!
From Documentation:
Prepares the receiver for service after it has been loaded from an
Interface Builder archive, or nib file. The nib-loading infrastructure
sends an awakeFromNib message to each object recreated from a nib
archive, but only after all the objects in the archive have been
loaded and initialized. When an object receives an awakeFromNib
message, it is guaranteed to have all its outlet and action
connections already established. You must call the super
implementation of awakeFromNib to give parent classes the opportunity
to perform any additional initialization they require. Although the
default implementation of this method does nothing, many UIKit classes
provide non-empty implementations. You may call the super
implementation at any point during your own awakeFromNib method.

One xib, several sub classes

I have one xib file for a custom UIView subclass. Works fine. I'm able to load the correct nib and create an instance of my class and it contains all the subviews I added to the xib file.
However, I have also subclassed this view, but I can't figure out how to create an instance of this class and get it to use the xib file used by the parent class. Is this even possible? I don't want to create a new xib file for my subclass, since the view hierarchy, subviews and GUI looks the same, it's just the code that differs.
Can I load a nib and "connect" it to another class than the one specified as the "Custom class" in the xib settings? Or can I create a new instance of a view and tell it to use a xib of a certain name?
You can try to write something really weird with -awakeAfterUsingCoder: to substitute created object, but this is really shaky and a few can get it right.
The thing is that .xib file stores set of serialised objects, when this set is loaded, information about each object, i.e. it's class, size, other attributes, parent object, constraints are as well deserialised and applied. So, xib files store which class should receive +alloc and other messages and, consequently, which objects will then receive all the attributes via KVC (-setValue:forKey:). So, no, you can't just configure some class to load some xib, because xib file tells which class should be loaded.
As a soulution I'd suggest to refactor your code, (for example) incapsulate different subclasses logic to some other object. So, before you had multiple subclasses with different logic, then, you'll have single class, loadable from xib, but you have to set some MyDifferentLogicVariant1Implamentor entity to preserve different logic for 'different' classes.
Superclass - Subclass1 - Subclass2
vs
Superclass.differentLogic = DifferentLogicImplementor1
Superclass.differentLogic = DifferentLogicImplementor2

sending messages from a datasource object to a view objective c

I have a custom uiview subclass that gets the data it needs to display from a datasource which is a subclass of nsmanagedobject. Is there any way that the nsmanagedobject subclass can hold a pointer to the view? I would like to do this so that the view controller can get some information about the frame size of the uiview by asking the datasource how much space its view takes up.
The major flaw in the design you are describing is that you have a UIView communicating directly with a data object. This goes against the basics of MVC and it's very likely you can achieve whatever it is you need by introducing a controller between these two elements. Please elaborate a little more about the frame size you're trying to calculate for a more detailed answer.

Resources