Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding a possibility to NibLoadable custom views to be automatically loaded when referenced from another XIB or Storyboard #105

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Skoti
Copy link
Contributor

@Skoti Skoti commented Apr 18, 2020

Hi

I would like to add a possibility for NibLoadable views to be automatically loaded when referenced from another XIB or Storyboard.
The current approach in Reusable is to only use NibOwnerLoadable views, but it produces an unnecessary nesting due to the fact that the file owner is also a UIView. I think the NibOwnerLoadable makes more sense for things like UIViewController where UIViewController is not the UIView itself but in fact the owner of a UIView instance.

In general this is the difference in layout:
image
MyCustomView is NibLoadable and its content: UILabel and UIView are not nested in the UIView as in the case of MyCustomWidget (NibOwnerLoadable)

Minor drawack is that it will break any constraints created in the XIB or Storyboard where NibLoadable view is referenced from (constraint outlets in NibLoadable XIB are safe and work just fine!). This is because the placeholder view in these other files is substituted with the real instance loaded from the XIB.
So NibLoadable and NibOwnerLoadable both have some pros and cons, but it would be great to use Reusable and have a choice :)

…loaded when referenced from another XIB or Storyboard
@AliSoftware
Copy link
Owner

Hello and thanks for the PR.

Can you explain in more details the pros and cons of this approach? If I understand your explanation correctly:

  • The only "Pro" I see in your explanation is that you… don't have to set a UIView as the File's Owner because you think File's Owners should only be UIViewControllers?
  • While I see multiple cons with this approach of trying to substitute the placeholder, including the dance you have to do with your layout constraints, and the complexity in code it brings

In the current way the library is working, we never do view substitutions – so we never risk breaking existing constraints or outlets. What we do instead is:

  • If you use NibOwnerLoadable, it will load its content inside an existing view (the owner, aka the placeholder view). This is suitable for views you want to be loaded in XIBs automatically
  • If you use NibLoadable, it will return the view from your XIB directly. This is suitable if you want to instantiate the view from code and get that view as a return value (e.g. to store it in a property, or pass it around, or add it as a child view in your SwiftUI hierarchy via a host view, etc

Using NibLoadable to make the view auto-loaded by a XIB is not what it's designed for, and more importantly is not how the XIB loading mechanism of iOS (UINib and all the iOS system methods which unarchive XIB and Storyboard files internally) is meant to be used. Instead, any view you put in your XIB or Storyboard is instantiated by iOS's XIB loading mechanism (that is, its initWithCoder method is called and the view instance is created), and trying to switch and replace it with something from another XIB means that very connection to the original view will be lost. This concerns not only layout constraints like you point out, but also any other IBOutloet and IBInspectable properties of the original view, because you'll replace it with a completely new instance, throwing the old instance out and losing all its connections and properties you might have set on it in the XIB's inspector pane as well.

So in my humble optioning, this has a potential to break way more things for the user of the lib in unexpected ways (constraints but also inspector properties – IBInspectable or not –, outlets and collections…), leading to more obscure bugs and support tickets for people trying that route, while the only "benefit" is to allow you not to set the File's Owner in your XIB to the appropriate class, so I don't feel it's worth the trouble it might bring.

@Skoti
Copy link
Contributor Author

Skoti commented Sep 12, 2021

Hi, thanks for getting back to this PR.

If you use NibOwnerLoadable, it will load its content inside an existing view (the owner, aka the placeholder view). This is suitable for views you want to be loaded in XIBs automatically

That's exactly what I am trying to avoid -> to have more flattened view hierarchy, if one is using a lot of reusable XIBs, simple screens can get quite heavy.

This concerns not only layout constraints like you point out, but also any other IBOutloet and IBInspectable properties of the original view, because you'll replace it with a completely new instance, throwing the old instance out and losing all its connections and properties you might have set on it in the XIB's inspector pane as well.

You misunderstood me. It does not break constraints created in the XIB of this NibLoadable. It only breaks those created in other XIB or Storyboard this NibLoadable is referenced in to be automatically loaded, and it only concerns constraints.
As you can see below setting yellow color for IBInspectable rectColor property in the Main.storyboard works without any issues for MyCustomView (which is NibLoadable) automatically loaded from a XIB:

All constraints/iboutlet/ibinspectable properties in XIB of this NibLoadable are correctly set as this is exactly what awakeAfter(using coder: NSCoder) -> Any? should be used for, documentation says:

Overridden by subclasses to substitute another object in place of the object that was decoded and subsequently received this message.


Using NibLoadable to make the view auto-loaded by a XIB is not what it's designed for,

True, but at the same time the owner-loading mechanism is also not designed for that.
Both were designed to be used in code, with the difference that File Owner is meant for view controllers because that's what you're actually instantiating -> a separate object which is not UIView, but manages such an instance.
From the docs:

One of the most important objects in a nib file is the File’s Owner object. Unlike interface objects, the File’s Owner object is a placeholder object that is not created when the nib file is loaded. Instead, you create this object in your code and pass it to the nib-loading code. The reason this object is so important is that it is the main link between your application code and the contents of the nib file. More specifically, it is the controller object that is responsible for the contents of the nib file.

"you create this object in your code and pass it to the nib-loading code" -> exactly the case for UIViewControllers, we instantiate them and then their contents (main view and everything else) is loaded when needed by the File Owner = UIViewController.
Thus using NibOwnerLoadable for autoloading views in XIBs results in an unnecessary UIView in the hierarchy because the File Owner is not meant to be an interface object itself, on the other hand using NibLoadable has the minor restriction of not using constraint outlets in the outer container (XIB/Storyboard).

In my opinion, this drawback is insignificant.
More over NibOwnerLoadable is actually more confusing. One might think that creating an outlet to NibOwnerLoadable view from a XIB/Storyboard such view is referenced in, will give them a reference to the loaded view, for example to operate on its properties, where in fact all they get is this unnecessary UIView instance. So all UIView related calls (like background color etc.) will affect the top-level view instead of the inner view of this reusable (view that has been added as a subview).
So changing properties related to UIView might not get the desired effect (i.e. backgroundColor which can be totally covered by the real inner/nested UIView that was automatically loaded and might have its own backgroundColor or other properties configured).
This duality of File Owner and the view is misleading (again it is clear for view controllers).
So for NibOwnerLoadable one needs to remember that some uses cases should be called on: view.subviews.first to achieve the desired effect.


There is another advantage of this approach, it makes possible to use `NibLoadable` in both other XIBs and in code, without creating this artificial FIle Owner.

More over this approach is totally opt-in, as one just need to manually add:

  override func awakeAfter(using coder: NSCoder) -> Any? {
    return self.awakeAfterUsingSurrogate()
  }

in the desired view. But that's all, it is that simple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants