0

I built a custom control for Xamarin.iOS. It's just a switch with a label. You can press anywhere on the control to toggle the switch.

The switch works great and I can see it at Runtime, but I was hoping not to confuse future users of the custom control. The control is nothing but a blank box or a red box with an exception at design time, and I can't seem to figure out how to get it to show up. The documentation makes no mention of using a XIB file to design the visual elements of your control.

I did a little digging and found an objective-c question about this problem: Failed to render instance of ClassName: The agent threw an exception loading nib in bundle

Unfortunately I can't find a C# equivalent of the code mentioned. What needs to be done to get my control to show at Design Time in the Xamarin iOS designer?

Here's the code behind:

/// <summary>
/// Labeled switch is a custom control
/// </summary>
[DesignTimeVisible(true)]
public partial class LabeledSwitch : UIView, IComponent
{
    public LabeledSwitch (IntPtr handle) : base (handle)
    {
        Initialize(CGRect.Empty);
    }

    public LabeledSwitch (CGRect frame) : base(frame)
    {
        Initialize(frame);
    }

    #region IComponent Impl

    public ISite Site { get ; set; }
    public event EventHandler Disposed;

    #endregion
    [Export("Text"), Browsable(true)]
    public string Text { get; set; }

    public UISwitch LabelSwitch { get; set; }

    public override void AwakeFromNib()
    {
        base.AwakeFromNib();
        Initialize(CGRect.Empty);
    }

    private void Initialize(CGRect frame)
    {

        if ((Site != null) && Site.DesignMode)
        {
            // Bundle resources aren't available in DesignMode?
            // Thought this might work but it does nothing
            var bundle = NSBundle.FromIdentifier("LabeledSwitch");
            bundle.LoadNib("LabeledSwitch", this, null);
            return;
        }

        BackgroundColor = UIColor.Clear;
        NSBundle.MainBundle.LoadNib("LabeledSwitch", this, null);

        AddSubview(RootView);

        SetTouchBehaviorOfLabeledSwitch();

        Frame = Bounds;
        RootView.Frame = Bounds;

        Label.Text = Text;

        LabelSwitch = Switch;

        HeightAnchor.ConstraintEqualTo(40f);
        RefreshSwitchBackground();
    }

    private void SetTouchBehaviorOfLabeledSwitch()
    {
        BackgroundView.TouchUpInside += TouchedLabeledSwitch;
    }

    private void TouchedLabeledSwitch(object sender, EventArgs e)
    {
        Switch.SetState(!Switch.On, true);
        RefreshSwitchBackground();
    }

    private void RefreshSwitchBackground()
    {
        if (Switch.On)
        {
            BackgroundView.BackgroundColor = UIColor.FromRGB(191, 249, 191);
            return;
        }
        BackgroundView.BackgroundColor = UIColor.FromRGB(239, 239, 244);
    }
}

And here's the xib:

<?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="LabeledSwitch">
            <connections>
                <outlet property="BackgroundView" destination="4" id="name-outlet-4"/>
                <outlet property="Switch" destination="5" id="name-outlet-5"/>
                <outlet property="Label" destination="6" id="name-outlet-6"/>
                <outlet property="RootView" destination="1" id="name-outlet-1"/>
            </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" customColorSpace="calibratedWhite" colorSpace="calibratedWhite" white="0" alpha="0"/>
            <subviews>
                <view contentMode="scaleToFill" id="4" translatesAutoresizingMaskIntoConstraints="NO" customClass="UIControl">
                    <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
                    <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
                    <userDefinedRuntimeAttributes>
                        <userDefinedRuntimeAttribute keyPath="layer.cornerRadius" type="number">
                            <real key="value" value="5"/>
                        </userDefinedRuntimeAttribute>
                    </userDefinedRuntimeAttributes>
                    <subviews>
                        <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" id="5" translatesAutoresizingMaskIntoConstraints="NO" userInteractionEnabled="NO">
                            <rect key="frame" x="533" y="285" width="51" height="31"/>
                            <constraints>
                                <constraint id="16" firstItem="5" firstAttribute="height" constant="31"/>
                                <constraint id="17" firstItem="5" firstAttribute="width" constant="49"/>
                            </constraints>
                            <accessibility key="accessibilityConfiguration">
                                <accessibilityTraits key="traits" button="YES" notEnabled="YES"/>
                            </accessibility>
                            <connections>
                                <action selector="HandleSwitchStateChanged:" destination="-1" id="20" eventType="valueChanged"/>
                            </connections>
                        </switch>
                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6" translatesAutoresizingMaskIntoConstraints="NO">
                            <rect key="frame" x="16" y="290" width="42" height="21"/>
                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
                            <color key="textColor" colorSpace="calibratedWhite" white="0.333333333333333" alpha="1"/>
                            <nil key="highlightedColor"/>
                            <constraints>
                                <constraint id="8" firstItem="6" firstAttribute="height" constant="21"/>
                            </constraints>
                            <accessibility key="accessibilityConfiguration">
                                <accessibilityTraits key="traits" staticText="YES" notEnabled="YES"/>
                            </accessibility>
                        </label>
                    </subviews>
                    <constraints>
                        <constraint id="7" firstItem="6" firstAttribute="leading" secondItem="4" secondAttribute="leading" constant="16"/>
                        <constraint id="9" firstItem="6" firstAttribute="centerY" secondItem="4" secondAttribute="centerY"/>
                        <constraint id="14" firstItem="4" firstAttribute="trailing" secondItem="5" secondAttribute="trailing" constant="18"/>
                        <constraint id="15" firstItem="5" firstAttribute="centerY" secondItem="4" secondAttribute="centerY"/>
                    </constraints>
                </view>
            </subviews>
            <constraints>
                <constraint id="10" firstItem="4" firstAttribute="top" secondItem="1" secondAttribute="top"/>
                <constraint id="11" firstItem="4" firstAttribute="leading" secondItem="1" secondAttribute="leading"/>
                <constraint id="12" firstItem="4" firstAttribute="bottom" secondItem="1" secondAttribute="bottom"/>
                <constraint id="13" firstItem="4" firstAttribute="trailing" secondItem="1" secondAttribute="trailing"/>
            </constraints>
        </view>
    </objects>
    <resources>
        <!-- ... Image tags are here-->
    </resources>
</document>

Exception in designer

aufty
  • 407
  • 2
  • 9
  • 26

1 Answers1

0

I've had to mess around with this a little myself, Xamarin isn't always perfect, I find re-registering the class sometimes forces it to appear in the toolbox and in the storyboard. (although I don't believe you should have to do this). This code would sit above your class and within your namespace.

[DesignTimeVisible(true), Category("Controls")]
[Register("MyView")]

If you ever have any concerns over how you should be formatting and creating controls theres an awesome github Here. These are all open source user and xamarin contributed controls, really well put together, and if you look at some of the classes can give you the missing bits of information about how to properly create design time viewable controls in Xamarin.

JoeTomks
  • 3,243
  • 1
  • 18
  • 42
  • The control already shows up in the toolbox, and I can drag and drop / use it in storyboards and xibs. The problem is that the control just shows either a clear box or a red box with a reference to the exception from the objective-c article. I've included a screenshot – aufty Jun 30 '17 at 12:51