Show Master on UISplitViewController by Default - xamarin.ios

When I create a new "Master Detail App" with Visual Studio for Mac (v8.4.5), the default behavior of the UISplitViewController is to show the Detail page first when it appears on an iPhone in Portrait mode.
I would rather (as I think most people would rather) have the Master page show by default. In my case, the Master page is a table view that holds a list of contacts.
This question is similar to: UISplitViewController in portrait on iPhone shows detail VC instead of master but for Xamarin.iOS
Similar to the solutions suggested there, I have attempted to assign a delegate without success:
public class ContactsSplitViewControllerDelegate : UISplitViewControllerDelegate
{
public override bool EventShowViewController(UISplitViewController splitViewController, UIViewController vc, NSObject sender)
{
return true;
}
public override bool EventShowDetailViewController(UISplitViewController splitViewController, UIViewController vc, NSObject sender)
{
return true;
}
}
public partial class ContactsSplitViewController : UISplitViewController
{
public ContactsSplitViewController (IntPtr handle) : base (handle)
{
this.Delegate = new ContactsSplitViewControllerDelegate();
}
}

set the PreferredDisplayMode
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.PreferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible;
}

After some experimentation, it seems that overriding CollapseSecondViewController on the delegate will work though I'm not yet convinced it's the proper solution.
using Foundation;
using System;
using UIKit;
namespace MasterDetailTest
{
public class SplitViewControllerDelegate : UISplitViewControllerDelegate
{
public override bool CollapseSecondViewController(UISplitViewController splitViewController, UIViewController secondaryViewController, UIViewController primaryViewController)
{
return true;
}
}
public partial class MainPageSplitViewController : UISplitViewController
{
public MainPageSplitViewController (IntPtr handle) : base (handle)
{
this.Delegate = new SplitViewControllerDelegate();
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// When implemented in my project, I found I needed to set this
// or the delegate would not be called.
this.SetNeedsFocusUpdate();
}
}
}

Related

Xamarin iOS crashes in NSBundle.MainBundle.LoadNib("CustomView", this, null)

I have created new CustomView.xib file and associated to code-behind class. But when i try to initialise the view, the app getting crashed as below mentioned error.
MonoTouch: Could not install sigaction override, unexpected sigaction implementation.
NSBundle.MainBundle.LoadNib("CustomView", this, null); // App crashes here.
CS class file:
public partial class CustomView : UIView
{
public CustomView(IntPtr handle) : base(handle)
{
}
public override void AwakeFromNib()
{
base.AwakeFromNib();
}
}
Designer class:
[Register("CustomView")]
partial class CustomView
{
[Outlet]
UIKit.UIView contentView { get; set; }
void ReleaseDesignerOutlets ()
{
if (contentView != null) {
contentView.Dispose ();
contentView = null;
}
}
}
Note:
Build Action is set to interfaceDefinition.
File owner of the xib is set the class name "CustomView"
AwakeFromNib override method also implemented.
Thanks in advance.
Edited:
We came across with similar kind of issue after a long time but with different exceptions says "Object Null Reference" on "contentView" in the AwakeFromNib method. App crashes when you try to access the ContentView inside the AwakeFromNib method. This happened only on iOS 9.3.x. The following fix might help someone if they face the same kind of issue.
[Export("initWithFrame:")]
public SampleHeaderView(CGRect frame) : base(frame)
{
Initialize();
}
[Export("initWithCoder:")]
public SampleHeaderView(NSCoder coder) : base(coder)
{
Initialize();
}
public override void AwakeFromNib()
{
base.AwakeFromNib();
}
private void Initialize()
{
if (this.ContentView != null)
{
this.ContentView.BackgroundColor = UIColor.Red();
}
}

ShowViewModel does not work if called too early

In MvvmCross 4.1.4 for Window Universal App (UWP) platform, if we call ShowViewModel too early within ViewModel (like in Constructor, Init, or Start event) then it does not navigate to another model.
public class FirstViewModel : MvxViewModel
{
public FirstViewModel()
{
ShowViewModel<SecondViewModel>();
}
}
Note that it works just fine for iOS and Android platform.
It is a bug of MvvmCross (according to this https://github.com/MvvmCross/MvvmCross/issues/1223).
The work around solution is to trigger the navigation from some events like View_Loaded or View_GotFocus within the View:
public sealed partial class FirstView : MvxWindowsPage
{
public FirstView()
{
this.InitializeComponent();
this.Loaded += FirstView_Loaded;
}
private void FirstView_Loaded(object sender, RoutedEventArgs e)
{
var viewModel = base.ViewModel as FirstViewModel
if (viewModel != null)
{
viewModel.Initialise();
}
}
}
ViewModel updated:
public class FirstViewModel : MvxViewModel
{
public FirstViewModel()
{
}
public void Initialise()
{
//Navigate here
ShowViewModel<SecondViewModel>();
}
}

Xamarin CollectionViewCell not instantiating from storyboard

I am collaborating with a designer who styles elements on my Xamarin project storyboard. I have trouble referencing his carefully placed elements in code. All the outlets are created, but in a UICollectionViewCell the UILabels etc are not instantiated. Here is code from a simple test app to demonstrate the problem.
The Xamarin generated code-behind:
[Register ("CardCell")]
partial class CardCell
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UILabel txtName { get; set; }
void ReleaseDesignerOutlets ()
{
if (txtName != null) {
txtName.Dispose ();
txtName = null;
}
}
}
My attempt to set the UI properties which causes the crash:
public partial class CardCell : UICollectionViewCell
{
public CardCell (IntPtr handle) : base (handle)
{
}
public void Update(string name)
{
txtName.Text = name; // throws exception here, because textName is null
}
}
The view controller with delegate methods:
public partial class TestCollectionController : UICollectionViewController
{
static NSString cardCellId = new NSString ("cardcell");
string[] cards = new string[] {"Red", "Green", "White", "Blue", "Pink", "Yellow"};
public TestCollectionController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
CollectionView.RegisterClassForCell (typeof(CardCell), cardCellId);
}
public override nint NumberOfSections (UICollectionView collectionView)
{
return 1;
}
public override nint GetItemsCount (UICollectionView collectionView, nint section)
{
return (nint)cards.Length;
}
public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
{
CardCell cell = (CardCell)collectionView.DequeueReusableCell (cardCellId, indexPath);
var card = cards [indexPath.Row];
cell.Update(card);
return cell;
}
}
I have tried both the Xamarin iOS Designer and Xcode's Interface Builder, but it does not make a difference. Any help will be greatly appreciated.
Examples of collection views driven by a storyboard are quite scarce, but I found this one and teased through it meticulously till I discovered that it did not include the call to register a class for the cell. So, remove this line:
CollectionView.RegisterClassForCell (typeof(CardCell), cardCellId);
and suddenly everything works as expected.
I was following this recipe and other examples, which all had the call included, which is only needed when you don't use a storyboard.

Monotouch.Dialog: crash with EnableSearch and custom RootElement

i use Monotouch.Dialog (Xamarin.iOS Version: 6.2.0.65) to create a RadioGroup with RadioElements. I only want the 'search' functionality in the second screen (where the user selects from the long list), so i created a separate DialogViewController where i enabled the search filter (enableSearch = true).
When typing in the search box, the app crashes in the GetCell method of the RadioElement.
"Object reference not set to an instance of an object" on line 1064 of Element.cs. Do i have GC problems? I managed to simulate it in a small app... Don't pay attention some weird field members, thats me testing if i'm begin GC...
using MonoTouch.Dialog;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace SearchCrash
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
// class-level declarations
UIWindow window;
public UINavigationController NavigationController { get; set; }
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
this.NavigationController = new UINavigationController();
this.NavigationController.PushViewController(new FirstViewController(this.NavigationController), true);
window.RootViewController = this.NavigationController;
window.MakeKeyAndVisible();
return true;
}
}
public class FirstViewController : DialogViewController
{
private UINavigationController controller;
private LocatiesRootElement locRootElement;
private RadioGroup radioGroup;
public FirstViewController(UINavigationController c) : base(new RootElement("Test"), true)
{
this.controller = c;
}
public override void ViewDidLoad()
{
Section section = new Section();
radioGroup = new RadioGroup(0);
locRootElement = new LocatiesRootElement("test", radioGroup, this.controller);
section.Add(locRootElement);
Root.Add(section);
}
}
public class LocatiesRootElement : RootElement
{
private UINavigationController navigationController;
private LocatiesViewController locatiesViewController;
public LocatiesRootElement(string selectedLocatie, RadioGroup group, UINavigationController controller) : base("Locatie", group)
{
this.navigationController = controller;
this.locatiesViewController = new LocatiesViewController(this);
}
public override void Selected(DialogViewController dvc, UITableView tableView, NSIndexPath path)
{
this.navigationController.PushViewController(this.locatiesViewController, true);
}
}
public class LocatiesViewController : DialogViewController
{
public LocatiesViewController(LocatiesRootElement root) : base(root, true)
{
this.EnableSearch = true;
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Section sectionList = new Section();
RadioElement radioElement1 = new RadioElement("item 1");
RadioElement radioElement2 = new RadioElement("item 2");
sectionList.Add(radioElement1);
sectionList.Add(radioElement2);
Root.Add(sectionList);
}
}
}
Try enabling the search after you instantiate the DialogViewController.
Like this :
public LocatiesRootElement(string selectedLocatie, RadioGroup group, UINavigationController controller) : base("Locatie", group)
{
this.navigationController = controller;
this.locatiesViewController = new LocatiesViewController(this);
this.locatiesViewController.EnableSearch = true;
}
Hopefully that will work for you.
The bug report here includes a workaround for the root cause of the issue you're experiencing, but also talks about how filtering will then cause a usability issue of marking the nth element as selected even after a filter has been applied.
https://github.com/migueldeicaza/MonoTouch.Dialog/issues/203
If you don't want to update the core MTD code, you could use that same technique by putting it in your own UIBarSearchDelegate. Unfortunately, the default SearchDelegate class is internal, so you'll need to add all of the code in your delegate. I was able to do this and get it working without changing the MTD source:
public override void LoadView()
{
base.LoadView();
((UISearchBar)TableView.TableHeaderView).Delegate = new MySearchBarDelegate(this);
}
And then you use this instead of the base method:
public override void TextChanged (UISearchBar searchBar, string searchText)
{
container.PerformFilter (searchText ?? "");
foreach (var s in container.Root)
s.Parent = container.Root;
}

UiWebView crashes iPhone

The following view crashes the iPhone
If I comment out the
using (webView = new UIWebView(webRect)) {
}
section - it does not.
using System;
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using System.Drawing;
namespace WmcStarClient
{
public class EventDetailView : UIViewController
{
public WmcStarEventService.Event _event;
public UIWebView webView;
public EventDetailView ()
{
}
public EventDetailView (WmcStarEventService.Event eventParam)
{
_event = eventParam;
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var webRect = new RectangleF(0f, 0f, 320f, 460f);
using (webView = new UIWebView(webRect)) {
}
}
}
}
has anyone seen this before?
w://
This was a bug that was fixed in the first beta of the 1.5 series:
http://monotouch.net/Releases/MonoTouch_1.4.99
When you're using the "using" construct, you are calling Dispose() on the webview, which is mapped to the "release" message in the Cocoa runtime.
So you are releasing the webview object, but you're still holding on to the reference in your class. The first time you try to access it, you're accessing a released instance.
Aren't you adding the webview to your view as a subview? If so, at what point?
The correct code, IMHO, would be:
public class EventDetailView : UIViewController
{
public WmcStarEventService.Event _event;
public UIWebView webView;
public EventDetailView ()
{
}
public EventDetailView (WmcStarEventService.Event eventParam)
{
_event = eventParam;
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var webRect = new RectangleF(0f, 0f, 320f, 460f);
webView = new UIWebView(webRect);
View.AddSubView(webView);
}
protected override void Dispose(bool disposing)
{
if (disposing)
webView.Dispose();
base.Dispose(disposing);
}
}
Note that overriding Dispose() is not required, but it should help to release memory earlier than simply relying on the garbage collector.
This is potentialy a different/new bug - i've gotten a better response over at the monotouch forums:
http://forums.monotouch.net/yaf_postsm1500.aspx#post1500
w://
It seems very strange to me to wrap that in a using statement. It's as if your disposing something while still loading. Are you sure that's right?

Resources