How to create a custom control (UIView) with XIB so that it's available in Xamarin iOS designer?

PeteRsnenPeteRsnen Pete RäsänenFIMember

Hi,

I'm trying to create a custom control which loads its UI from xib, and in addition so that it would be available and renderable under "Custom components" in Xamarin Studio Designer. I have followed these tutorials:

http://developer.xamarin.com/guides/ios/user_interface/designer/ios_designable_controls_overview/
http://developer.xamarin.com/recipes/ios/general/templates/using_the_ios_view_xib_template/

which seem to be a bit outdated (?). Anyway, I got to the point where my view is shown under Custom components in Toolbox but when i drag it to any storyboard, I get the attached error (NSInternalnconsistencyException, and clicking that error crashes Xamarin Studio). I am using Xamarin Studio with Mac.

Is anyone able to help? A working code example of how such component is done would be very helpful. I think this kind of feature is quite essential for professional development..

Best Regards, Pete

Answers

  • DmytroBondarenkoDmytroBondarenko Dmytro Bondarenko USMember ✭✭

    up

  • NathanWilliamsNathanWilliams Nathan Williams USUniversity

    Here's what I did:

    1. Add a new iOS View to your project, e.g. 'CustomView';
    2. Open the newly added xib and in the properties, set the "File's Owner" Class, e.g. 'CustomView' -- this will generate a CustomView.cs and code-behind file;
    3. Then select the root View and in the properties, set the "Name", e.g. 'rootView' (NOTE: don't use 'view' as this appears to conflict with internals) -- this will expose it in the code-behind;
    4. Likewise, you can set the "Name" of any other controls to expose them via the code-behind;
    5. Now open the newly added CustomView.cs file (or whatever you called the "File's Owner" Class) and configure it as per the example below:
        [DesignTimeVisible(true)]  // This makes it visible in the Custom Controls panel in the iOS Designer.  The code-behind file already has the Register attribute 
        public partial class CustomView : UIView, IComponent  // IComponent is necessary to check DesignMode
        {
            public CustomView(IntPtr handle) : base (handle)
            {
            }
    
            #region IComponent implementation
    
            public ISite Site { get; set; }
            public event EventHandler Disposed;
    
            #endregion
    
            // This is an example of exposing a custom property that can be set via the iOS Designer
            [Export("Name"), Browsable(true)]
            public string Name { get; set; }
    
            public override void AwakeFromNib()
            {
                base.AwakeFromNib();
    
                if ((Site != null) && Site.DesignMode)
                {
                    // Bundle resources aren't available in DesignMode
                    return;
                }
    
                NSBundle.MainBundle.LoadNib("CustomView", this, null);
    
                // At this point all of the code-behind properties should be set, specifically rootView which must be added as a subview of this view
    
                this.AddSubview(this.rootView);
    
                this.nameLabel.Text = Name;  // ...and here's how you can use custom properties to set values on the code-behind properties
            }
        }
    

    Rebuild the project and your CustomView should now be visible in the Custom Controls of the iOS Designer. Note, however that you will not get rendering of the actual content in the Designer.

  • DipenHansawalaDipenHansawala Dipen Hansawala USMember
    edited October 2016

    Hey, Can you please give me sample project for this ? :smile:

  • ChristophKahlChristophKahl Christoph Kahl DEMember ✭✭

    @NathanWilliams said:
    ...
    NSBundle.MainBundle.LoadNib("CustomView", this, null);
    ...

    Here crashes my application and I got a StackOverflow exception. Can you please show us your CustomView.xib file? Which properties did you set there inside?

  • UserName.3183UserName.3183 User Name ILMember
    edited December 2016

    @ChristophKahl said:

    @NathanWilliams said:
    ...
    NSBundle.MainBundle.LoadNib("CustomView", this, null);
    ...

    Here crashes my application and I got a StackOverflow exception. Can you please show us your CustomView.xib file? Which properties did you set there inside?

    After having the same issue, there were few things that weren't clear to me and caused the error.
    I'll write it here in case your case (or other that will get to this post) is the same.
    1. It is critical to set the class property of the custom view to be UIView or just leave it empty. It shouldn't be from type "CustomView".
    2. Instead, We use the Owner property to create the connection between the xib file and the cs file. To get to this property, open the xib using the designer (Xamarin's) and just tap on the empty area outside of the xib (or the blue frame of the xib). The properties widows should look like this:

    1. rootView is the top view within the xib file and we need to name it rootView (or any other name).


    Going through these steps fixed the issue for me.

  • UserName.3183UserName.3183 User Name ILMember

    And here are the files:

    CustomView.designer.cs:

    </p> <pre><code>// WARNING // // This file has been generated automatically by Xamarin Studio from the outlets and // actions declared in your storyboard file. // Manual changes to this file will not be maintained. // using Foundation; using System; using System.CodeDom.Compiler; using UIKit; [Register ("CustomView")] partial class CustomView { [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UILabel nameLabel { get; set; } [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UIView rootView { get; set; } void ReleaseDesignerOutlets () { if (nameLabel != null) { nameLabel.Dispose (); nameLabel = null; } if (rootView != null) { rootView.Dispose (); rootView = null; } } }

    CustomView.cs:

    </p> <pre><code>using Foundation; using System; using UIKit; using System.ComponentModel; [DesignTimeVisible (true)] // This makes it visible in the Custom Controls panel in the iOS Designer. The code-behind file already has the Register attribute public partial class CustomView : UIView, IComponent // IComponent is necessary to check DesignMode { public CustomView (IntPtr handle) : base (handle) { } #region IComponent implementation public ISite Site { get; set; } public event EventHandler Disposed; #endregion // This is an example of exposing a custom property that can be set via the iOS Designer [Export ("Name"), Browsable (true)] public string Name { get; set; } public override void AwakeFromNib () { base.AwakeFromNib (); if ((Site != null) && Site.DesignMode) { // Bundle resources aren't available in DesignMode return; } NSBundle.MainBundle.LoadNib ("CustomView", this, null); // At this point all of the code-behind properties should be set, specifically rootView which must be added as a subview of this view this.AddSubview (this.rootView); this.nameLabel.Text = Name; // ...and here's how you can use custom properties to set values on the code-behind properties } }

    CustomView.xib:

    </p> <pre><code><?xml version="1.0" encoding="UTF-8" standalone="no"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"> <dependencies> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CustomView"> <connections> <outlet property="rootView" destination="1" id="name-outlet-1"/> <outlet property="nameLabel" destination="4" id="name-outlet-4"/> </connections> </placeholder> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <view contentMode="scaleToFill" id="1"> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4" translatesAutoresizingMaskIntoConstraints="NO" fixedFrame="YES"> <rect key="frame" x="90" y="66" width="42" height="21"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> </subviews> </view> </objects> <resources> </resources> </document>

  • UserName.3183UserName.3183 User Name ILMember

    And here are the files:

    CustomView.designer.cs:

    </p> <pre><code>// WARNING // // This file has been generated automatically by Xamarin Studio from the outlets and // actions declared in your storyboard file. // Manual changes to this file will not be maintained. // using Foundation; using System; using System.CodeDom.Compiler; using UIKit; [Register ("CustomView")] partial class CustomView { [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UILabel nameLabel { get; set; } [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UIView rootView { get; set; } void ReleaseDesignerOutlets () { if (nameLabel != null) { nameLabel.Dispose (); nameLabel = null; } if (rootView != null) { rootView.Dispose (); rootView = null; } } }

    CustomView.cs:

    </p> <pre><code>using Foundation; using System; using UIKit; using System.ComponentModel; [DesignTimeVisible (true)] // This makes it visible in the Custom Controls panel in the iOS Designer. The code-behind file already has the Register attribute public partial class CustomView : UIView, IComponent // IComponent is necessary to check DesignMode { public CustomView (IntPtr handle) : base (handle) { } #region IComponent implementation public ISite Site { get; set; } public event EventHandler Disposed; #endregion // This is an example of exposing a custom property that can be set via the iOS Designer [Export ("Name"), Browsable (true)] public string Name { get; set; } public override void AwakeFromNib () { base.AwakeFromNib (); if ((Site != null) && Site.DesignMode) { // Bundle resources aren't available in DesignMode return; } NSBundle.MainBundle.LoadNib ("CustomView", this, null); // At this point all of the code-behind properties should be set, specifically rootView which must be added as a subview of this view this.AddSubview (this.rootView); this.nameLabel.Text = Name; // ...and here's how you can use custom properties to set values on the code-behind properties } }

    CustomView.xib:

    </p> <pre><code><?xml version="1.0" encoding="UTF-8" standalone="no"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"> <dependencies> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CustomView"> <connections> <outlet property="rootView" destination="1" id="name-outlet-1"/> <outlet property="nameLabel" destination="4" id="name-outlet-4"/> </connections> </placeholder> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <view contentMode="scaleToFill" id="1"> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4" translatesAutoresizingMaskIntoConstraints="NO" fixedFrame="YES"> <rect key="frame" x="90" y="66" width="42" height="21"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> </subviews> </view> </objects> <resources> </resources> </document>

  • UserName.3183UserName.3183 User Name ILMember

    And here are the files:

    CustomView.designer.cs:

    </p> <pre><code>// WARNING // // This file has been generated automatically by Xamarin Studio from the outlets and // actions declared in your storyboard file. // Manual changes to this file will not be maintained. // using Foundation; using System; using System.CodeDom.Compiler; using UIKit; [Register ("CustomView")] partial class CustomView { [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UILabel nameLabel { get; set; } [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UIView rootView { get; set; } void ReleaseDesignerOutlets () { if (nameLabel != null) { nameLabel.Dispose (); nameLabel = null; } if (rootView != null) { rootView.Dispose (); rootView = null; } } }

    CustomView.cs:

    </p> <pre><code>using Foundation; using System; using UIKit; using System.ComponentModel; [DesignTimeVisible (true)] // This makes it visible in the Custom Controls panel in the iOS Designer. The code-behind file already has the Register attribute public partial class CustomView : UIView, IComponent // IComponent is necessary to check DesignMode { public CustomView (IntPtr handle) : base (handle) { } #region IComponent implementation public ISite Site { get; set; } public event EventHandler Disposed; #endregion // This is an example of exposing a custom property that can be set via the iOS Designer [Export ("Name"), Browsable (true)] public string Name { get; set; } public override void AwakeFromNib () { base.AwakeFromNib (); if ((Site != null) && Site.DesignMode) { // Bundle resources aren't available in DesignMode return; } NSBundle.MainBundle.LoadNib ("CustomView", this, null); // At this point all of the code-behind properties should be set, specifically rootView which must be added as a subview of this view this.AddSubview (this.rootView); this.nameLabel.Text = Name; // ...and here's how you can use custom properties to set values on the code-behind properties } }

    CustomView.xib:

    </p> <pre><code><?xml version="1.0" encoding="UTF-8" standalone="no"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"> <dependencies> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CustomView"> <connections> <outlet property="rootView" destination="1" id="name-outlet-1"/> <outlet property="nameLabel" destination="4" id="name-outlet-4"/> </connections> </placeholder> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <view contentMode="scaleToFill" id="1"> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4" translatesAutoresizingMaskIntoConstraints="NO" fixedFrame="YES"> <rect key="frame" x="90" y="66" width="42" height="21"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> </subviews> </view> </objects> <resources> </resources> </document>

  • UserName.3183UserName.3183 User Name ILMember

    And here are the files:

    CustomView.designer.cs:

    </p> <pre><code>// WARNING // // This file has been generated automatically by Xamarin Studio from the outlets and // actions declared in your storyboard file. // Manual changes to this file will not be maintained. // using Foundation; using System; using System.CodeDom.Compiler; using UIKit; [Register ("CustomView")] partial class CustomView { [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UILabel nameLabel { get; set; } [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UIView rootView { get; set; } void ReleaseDesignerOutlets () { if (nameLabel != null) { nameLabel.Dispose (); nameLabel = null; } if (rootView != null) { rootView.Dispose (); rootView = null; } } }

    CustomView.cs:

    </p> <pre><code>using Foundation; using System; using UIKit; using System.ComponentModel; [DesignTimeVisible (true)] // This makes it visible in the Custom Controls panel in the iOS Designer. The code-behind file already has the Register attribute public partial class CustomView : UIView, IComponent // IComponent is necessary to check DesignMode { public CustomView (IntPtr handle) : base (handle) { } #region IComponent implementation public ISite Site { get; set; } public event EventHandler Disposed; #endregion // This is an example of exposing a custom property that can be set via the iOS Designer [Export ("Name"), Browsable (true)] public string Name { get; set; } public override void AwakeFromNib () { base.AwakeFromNib (); if ((Site != null) && Site.DesignMode) { // Bundle resources aren't available in DesignMode return; } NSBundle.MainBundle.LoadNib ("CustomView", this, null); // At this point all of the code-behind properties should be set, specifically rootView which must be added as a subview of this view this.AddSubview (this.rootView); this.nameLabel.Text = Name; // ...and here's how you can use custom properties to set values on the code-behind properties } }

    CustomView.xib:

    </p> <pre><code><?xml version="1.0" encoding="UTF-8" standalone="no"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"> <dependencies> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CustomView"> <connections> <outlet property="rootView" destination="1" id="name-outlet-1"/> <outlet property="nameLabel" destination="4" id="name-outlet-4"/> </connections> </placeholder> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <view contentMode="scaleToFill" id="1"> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4" translatesAutoresizingMaskIntoConstraints="NO" fixedFrame="YES"> <rect key="frame" x="90" y="66" width="42" height="21"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> </subviews> </view> </objects> <resources> </resources> </document>

  • sebnilsebnil Sebastian Nilsson SEMember

    I was struggeling to get CustomViews to run so I created a github repo with a working example:
    https://github.com/sebnil/XamarinCustomViewExample

    Maybe it will be useful for someone else. Thank you for helping me with some code snippets.

  • KratiChauhanKratiChauhan Krati Chauhan USMember ✭✭

    I am getting below error will trying to create a custom view at runtime.
    I get this error when trying to loadNib. "rootView" is the name of the the main UIView

    Objective-C exception thrown. Name: NSUnknownKeyException Reason: [ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key rootView.
    Native stack trace:

    Any help on this is much appreciated

Sign In or Register to comment.