3

I am experience the exact same issue as identified in this submission: MvvmCross/Xamarin "This class is not key value coding-compliant for the key"

The issue stems from trying to use MVVMCross databinding in an IOS CollectionView

That article was closed and marked as a duplicate but I don't see it as such. The article that it links to as the solution offers workarounds when using XCode as a development environment. My scenario and the scenario described in the issue linked above are in using Visual Studio on Windows with Xamarin to build with. With Visual Studio, the XIB editor and wiring up outlets is done differently.

I have been trying to get past this problem for a few days now and haven't figured anything out. I'm hoping that someone who has seen this in Windows Visual Studio has a solution that they can share.

(I apologize, I am new so I couldn't do this as a comment in the original post).

The Stack trace of the error is below

Foundation.MonoTouchException: Objective-C exception thrown.  
Name: NSUnknownKeyException Reason: [<NSObject 0x7c4eb9a0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key view.

Native stack trace:

0   CoreFoundation                      0x00928494 __exceptionPreprocess + 180
1   libobjc.A.dylib                     0x08fa3e02 objc_exception_throw + 50
2   CoreFoundation                      0x009280b1 -[NSException raise] + 17
3   Foundation                          0x014477f8 -[NSObject(NSKeyValueCoding) setValue:forUndefinedKey:] + 282
4   Foundation                          0x013a1e6a _NSSetUsingKeyValueSetter + 115
5   Foundation                          0x013a1def -[NSObject(NSKeyValueCoding) setValue:forKey:] + 295
6   Foundation                          0x013d654b -[NSObject(NSKeyValueCoding) setValue:forKeyPath:] + 384
7   UIKit                               0x03bc8a62 -[UIRuntimeOutletConnection connect] + 132
8   libobjc.A.dylib                     0x08fb800c -[NSObject performSelector:] + 62
9   CoreFoundation                      0x00851131 -[NSArray makeObjectsPerformSelector:] + 273
10  UIKit                               0x03bc70fc -[UINib instantiateWithOwner:options:] + 2102
11  UIKit                               0x041205ec -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] + 750
12  UIKit                               0x04120f35 -[UICollectionView dequeueReusableCellWithReuseIdentifier:forIndexPath:] + 194
13  ???                                 0x1b909f9c 0x0 + 462462876
14  ???                                 0x1be1b820 0x0 + 467777568
15  ???                                 0x1be1b633 0x0 + 467777075
16  ???                                 0x1be1adf6 0x0 + 467774966
17  ???                                 0x1be1b08d 0x0 + 467775629
18  CompanionForSpotifyiOS              0x001bb2a1 mono_jit_runtime_invoke + 705
19  CompanionForSpotifyiOS              0x00276fef mono_runtime_invoke + 127
20  CompanionForSpotifyiOS              0x0034bb77 xamarin_trampoline + 5559
21  UIKit                               0x0410d5ee -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:isFocused:] + 448
22  UIKit                               0x0410d2ff -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:] + 65
23  UIKit                               0x04111761 -[UICollectionView _updateVisibleCellsNow:] + 6023
24  UIKit                               0x041167df -[UICollectionView layoutSubviews] + 254
25  UIKit                               0x038543d4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 810
26  libobjc.A.dylib                     0x08fb8059 -[NSObject performSelector:withObject:] + 70
27  QuartzCore                          0x0b5d8096 -[CALayer layoutSublayers] + 144
28  QuartzCore                          0x0b5cb8b6 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 388
29  QuartzCore                          0x0b5cb71a _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 26
30  QuartzCore                          0x0b5bdee7 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 317
31  QuartzCore                          0x0b5f2847 _ZN2CA11Transaction6commitEv + 561
32  QuartzCore                          0x0b5f3108 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 92
33  CoreFoundation                      0x0083a75e __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
34  CoreFoundation                      0x0083a6be __CFRunLoopDoObservers + 398

The ViewModel code is here

public partial class AlbumCell : MvxCollectionViewCell
{
    public static readonly UINib Nib = UINib.FromName("AlbumCell", NSBundle.MainBundle);
    public static readonly NSString Key = new NSString("AlbumCell");

    public AlbumCell(IntPtr handle) : base(string.Empty, handle)
    {
        this.DelayBind(() =>
        {
            var set = this.CreateBindingSet<AlbumCell, Album>();
            set.Bind(artistNameLabel).To(album => album.ArtistName);
            set.Apply();
        });
    }

    public static AlbumCell Create()
    {
        return (AlbumCell)Nib.Instantiate(null, null)[0];
    }
}

And this is the generated component of the view model that has the outlets [Register ("AlbumCell")] partial class AlbumCell { [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UILabel artistNameLabel { get; set; }

    void ReleaseDesignerOutlets ()
    {
        if (artistNameLabel != null) {
            artistNameLabel.Dispose ();
            artistNameLabel = null;
        }
    }
}

Finally, this is the XIB which is very basic - it just has one label on it

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
        <dependencies>
            <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
        </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AlbumCell">
            <connections>
                <outlet property="view" destination="2" id="RRd-Eg-VrN"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="2">
            <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
            <subviews>
                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" text="Label" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="5">
                    <rect key="frame" x="279" y="95" width="42" height="21"/>
                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
        </view>
    </objects>
</document>

The code where I create instanced of the cell objects from the parent ViewController is below

public partial class NewReleasesView : MvxCollectionViewController
{
    public NewReleasesView()
        : base(new UIKit.UICollectionViewFlowLayout()
        {
            ItemSize = new CoreGraphics.CGSize(250,200),
            ScrollDirection = UICollectionViewScrollDirection.Vertical
        })
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        CollectionView.RegisterNibForCell(AlbumCell.Nib, AlbumCell.Key);
        var source = new MvxCollectionViewSource(CollectionView, AlbumCell.Key);
        CollectionView.Source = source;

        var set = this.CreateBindingSet<NewReleasesView, NewReleasesViewModel>();
        set.Bind(source).To(vm => vm.NewReleases);
        set.Apply();
        CollectionView.ReloadData();

        var viewModel = this.ViewModel as IMvxViewModel;
        viewModel.Start();
    }

}
Community
  • 1
  • 1
Michael Foster
  • 113
  • 1
  • 7
  • Check your outlets. That happens when you delete the outlet property from your code but you didn't remove the connection from the Storyboard. – Alejandro Iván May 29 '16 at 04:33
  • Can you post more detail of the exception message? What is that class? What is the key? Do you have a stack trace? Which line did your app crash on? – Paulw11 May 29 '16 at 04:34
  • @Paulw11, I added coding samples and the call stack to the description. thanks!! – Michael Foster May 29 '16 at 14:55
  • The exception says that you have an instance of `NSObject`, not an AlbumCell. Can you show the code where are you creating the cell instance? – Paulw11 May 29 '16 at 19:41
  • @Paulw11, I have updated the post above with the ViewController code that has the configuration for creating the cell objects. – Michael Foster May 29 '16 at 20:08
  • Can you show where you call `dequeueReusableCellWithIdentifier`? This should be in `cellForItemAtIndexPath` as this is where you are getting an NSObject. – Paulw11 May 29 '16 at 20:33
  • @Paulw11q, I dont make any direct calls to that myself. I think that is part of the MVVMCross framework and probably handled via the MvxCollectionViewSource which is a part of MVVMCross. – Michael Foster May 29 '16 at 23:36
  • I thought i solved the problem by calling CollectionView.RegisterClassForCell. But when i call that, the correct class is created but there is no XIB loaded. When I call CollectionView.RegisterNibForCell then the NIB is loaded but the associated Mvx CollectionViewCell derived is never instantiated – Michael Foster May 30 '16 at 04:12

2 Answers2

4

It looks like Visual Studio was putting the outlets in the wrong location. See below where i typed "Outlet was here". That is where the UI Label was generated. I manually removed it then went into the XIB Editor in visual studio and re-added the outlet name in the designer. At that point, it put it in it's right place where i labeled "When it should have been here".

I'm not sure what caused the behavior in the first place, but this work around definitely did the trick. Thanks to all who assisted me.

<objects>
    <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AlbumCell">
        <connections>
             ***OUTLET WAS HERE***
        </connections>
    </placeholder>
    <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
    <view contentMode="scaleToFill" id="1" customClass="AlbumCell">
        <rect key="frame" x="0.0" y="0.0" width="250" height="250"/>
        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
        <color key="backgroundColor" customColorSpace="calibratedWhite" colorSpace="calibratedWhite" white="0" alpha="1"/>
        <subviews>
            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Label" lineBreakMode="tailTruncation" minimumFontSize="10" id="4" translatesAutoresizingMaskIntoConstraints="NO" fixedFrame="YES" customClass="UILabel">
                <rect key="frame" x="53" y="20" width="177" height="21"/>
                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                <color key="textColor" colorSpace="calibratedWhite" white="1" alpha="1"/>
                <nil key="highlightedColor"/>
            </label>
        </subviews>
        <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
        <size key="freeformSize" width="600" height="600"/>
        <connections>
            ***WHEN IT SHOULD HAVE BEEN HERE***
            <outlet property="artistname" destination="4" id="name-outlet-4"/>
        </connections>
    </view>
</objects>
Michael Foster
  • 113
  • 1
  • 7
0

Thanks to the direction of @Paulw11q, I figured out the problem.

I had to call the following line along with the RegisterNibForCell so that MVVMCross would know the type of object it needed to create. That line was missing from all of the MVVMCross samples that I saw.

CollectionView.RegisterClassForCell(typeof(AlbumCell), AlbumCell.Key);
Paolo
  • 20,112
  • 21
  • 72
  • 113
Michael Foster
  • 113
  • 1
  • 7
  • turns out that this didn't fix the problem. that cause my Cell derived class to get created with no errors but then in return, the XIB was not loaded. i'm still stuck :( – Michael Foster May 30 '16 at 18:09