MonoTouch: add a custon view XIB - xamarin.ios

I would like to add a custom view XIB to my UITable views header. Following this link How to add a custom view to a XIB file defined view in monotouch i created a XIB with a view. It has just two labels in it.
I then have a cs file. Called XIB as mention in the link
public override UIView GetViewForHeader(UITableView tableView, int sectionIndex) {
var myHeader = new UIView();
var views = NSBundle.MainBundle.LoadNib("CustomHeaderView", this, null);
CustomHeaderView loginView = Runtime.GetNSObject(views.ValueAt(0)) as CustomHeaderView;
loginView.UpdateWithData("test","test");
myHeader.AddSubview(loginView);
return myHeader;
}
When i run the app it is crashing, dont see any error.

Make sure that your .XIB is not using AutoLayout and you are using an older iOS. The AutoLayout is new for 6.x and will cause a crash on the 5.x based devices. I see this quite often.

Related

Xamarin.iOS: How to add new ViewControllers to TabBarController FROM CODE, with each ViewController having its own .storyboard for UI?

I have a UITabBarController called MainView, in MainView.storyboard. I have 2 other UIViewControllers called ChildOneView and ChildTwoView, each in their own .storyboards, ChildOneView.storyboard and ChildTwoView.storyboard.
MainView.storyboard has only a TabBar and nothing else. Each Child storyboard contains only a Label with its name. There is no segue whatsoever; navigation is intended to be managed by MvvmCross.
What I want to achieve, and has no idea how to, is from MainView.cs, instantiate ChildOneView and ChildTwoView as new tab items in MainView, with ChildOneView and ChildTwoView having their UI loaded from their corresponding .storyboards. The navigation is managed by MvvmCross.
I have searched for an answer for doing those things from code, but I've only found ones that were about using a single storyboard with segues. Any tips on how to achieve this?
From code, you can create a new instance from different storyboard with:
UIStoryboard childOneSb = UIStoryboard.FromName("ChildOneView", null);
UIStoryboard childTwoSb = UIStoryboard.FromName("ChildTwoView", null);
Then you can create a new instance of your ViewControllers with:
ChildOneView_VC vc1 = childOneSb.InstantiateViewController("ChildOneView") as ChildOneView;
ChildTwoView_VC vc2 = childTwoSb.InstantiateViewController("ChildTwoView") as ChildTwoView;
And then you can put them in your UITabBarController.
PS: Remember to put ChildOneView and ChildTwoView as Storyboard ID in with XCode or Xamarin Designer

Developing Xamarin.iOS application using MVVMCross and Storyboards

I am trying to understand how storyboards work in iOS development and how MVVMCross fits in. I thought the best solution would be to build an iOS version of the MVVMCRoss TipCalc Tutorial
I am using Storyboards as you cannot edit XIBs in Visual Studio. My current thinking is one Storyboard per screen.
I have it working but it feels like i did it with more luck than judgement. Therefore I want to check my understanding.
In TipCalc.UI.Touch I have
TipViewController.cs
TipViewController.storyboard
I have added a custom Mvx View Container as suggested in this SO answer. In the CreateViewOfType method of that container I am calling Storyboard.InstantiateViewController and casting that to an IMvxTouchView.
How can a controller be a View as well?
I am planning on having a "View" per storyboard.
If you have multiple views in a storyboard, would you have a controller per view?
When I bring up the Properties window for a "View" in the storyboard designer it has a Name and a class in the Identity section. What is the purpose of the Class property? Does that create a code-behind file?
I am creating the View-to-ViewModel bindings in the ViewDidLoad method of the Controller
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.CreateBinding(this.tipValueText).To<TipViewModel>(vm => vm.Tip).Apply();
this.CreateBinding(this.subTotalTextBox).To<TipViewModel>(vm => vm.SubTotal).Apply();
this.CreateBinding(this.generositySlider).To<TipViewModel>(vm => vm.Generosity).Apply();
}
These bindings work but again I just wanted to check that it is how others do it too.
The iOS ViewController is actually the View in an MVVMCross application. You can think of the view controller as the code behind for the view (so just like a Windows Phone/Windows Store app will have a XAML and related .cs file, or an Android app will have an axml and a java view class)
Yes, when using multiple views in a single storyboard each one will actually be a viewcontroller (since that's what a screen in a storyboard is)
The class property defines which viewcontroller class the layout in the storyboard uses (so which code behind class to use, and if it doesn't exist it will be created)
I prefer a single storyboard as most of my apps don't have too many screens so these are the steps I follow when creating a View in an existing storyboard
Add a ViewController
Type in the view name in the Class field (this name would correspond to the ViewModel name, so HomeView for HomeViewModel, etc.). As soon as you hit enter the ViewController class should be created.
Type in the same view name as the Storyboard Id (this is used to fetch that view)
Add controls and set their names. Setting a control's name will updated the .designer file that was created for the ViewController
Create the ViewDidLoad override in the ViewController class and set up my bindings
If you use a storyboard per view, creating that storyboard with the correct name (HomeView for example) should create the ViewController and designer files for you and give you a storyboard with that one viewcontroller ready to go
edit:
in your bindings, you can use one set.Apply(); at the end

Customizing Xamarin.Forms layouts

I'm building an Android and iOS app using Xamarin Forms.
What I'm simply trying to do is set a background drawable on my Android app for my ListView items. The root view of my ListView items are StackLayout's:
var listView = new ListView
{
ItemsSource = items,
ItemTemplate = new DataTemplate(() =>
{
return new ViewCell
{
View = new StackLayout(...)
};
}
};
I know I can access the native element by using a custom renderer:
public class MyEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.OldElement == null) {
var nativeEditText = (EditText)Control;
...
}
}
}
But I'm not sure how this would work for a StackLayout (or any other layout for that matter).
I first extended StackLayout:
public class ListViewItem : StackLayout
{
}
And I read somewhere that layouts use the VisualElementRenderer, so I tried the following:
public class ListViewItemRenderer : VisualElementRenderer<StackLayout>
{
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
// any way to access the native element?
}
}
But VisualElementRenderer does not seem to give me access to the native element.
So is there any way I can access the native elements of Layout elements? Or maybe there is a different way to simply set a background drawable on layouts within my Android app?
Even though I still don't know how to access the native element of a layout, the VisualElementRenderer has a method for setting the background drawable on Android (which was exactly what I needed). So I ended up with the following:
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
SetBackgroundDrawable(Resources.GetDrawable(Resource.Drawable.listViewItem));
}
I understand you want to hook into an existing Layout renderer and extending it to access the native element with extra capabilities like background image.
Eventually the support for background-image will be supported just like background-colour is, I imagine, across the Layout controls. It may be worth while waiting for this as I can't see why they wouldn't implement these in a later release.
In the mean time you would need something that would work and is quite easy to implement?
Creating the background drawable via inheriting the renderer from a Layout may not be the simplest of solutions therefore, although does have its advantages as you can then re-use easily with the extra functionality across all Layouts for an application.
In your code for ListViewItemRenderer, however, it is inheriting from a Xamarin.Forms control (you specified StackLayout) and have not specified a native, platform dependent, control to be the base for the layout control that would have to match the Xamarin.Forms platform dependent control used.
Each Renderer is tied to a native element. Layout controls will be no different than other custom native control renderers.
For a custom control, you will write a renderer something like the following (note I haven't specified a layout renderer as I haven't had a need to do this yet and am just going from past experience - but similar rules should apply to implementing a renderer for a layout as opposed to a custom control):-
// System.Windows.Controls.Grid in this case is the root native control for a WindowsPhone renderer of MyControl
public class MyControlRenderer : ViewRenderer<MyControlView, System.Windows.Controls.Grid>
There is a simpler approach, however to achieve what you want to do:-
The simpler approach would be instead of inheriting from the Stack Layout control, it would be better to inherit from Grid as the root of the control.
Then you can add an Image control to the Grid and also a Stack Layout for the same Grid Row and Column.
By doing the above you will be able to achieve a background-image across the entire listview item row.

How do I do two-way binding to a UITextView control using mvvmcross?

I have a simple view I created using XCode (it's a XIB). It consists of two UITextView controls and a UIButton. I've exposed the UITextView controls as Outlets and given them names. Everything looks good in my View.designer.cs file.
I'm creating my binding using this syntax:
this.AddBindings(
new Dictionary<object, string>()
{
{lastname, "{'Text':{'Path':'LastName','Mode':'TwoWay'}}"},
{uservin, "{'Text':{'Path':'CarVIN','Mode':'TwoWay'}}"}
});
When I move from the lastname UITextView to the uservin UITextView I expect the SETTER in my viewmodel to get called, but it doesn't. When I click the button and check the value of the two text properties on my ViewModel, they are both null.
Does anybody know what I've done wrong? I've got a quick demo due on Tuesday and have all other platforms working beautifully, but just can't seem to get past this issue.
Cheers,
/j
I think the problem will be that you are using UITextView instances... but mvvmcross vnext only ships with 2-way binding for UITextField elements.
To add the UITextView 2-way binding to an older mvvmcross version:
Add a custom target binding class - a bit like this one from v3: https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Binding.Touch/Target/MvxUITextViewTextTargetBinding.cs
Register it during Setup.cs with code like:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxSimplePropertyInfoTargetBindingFactory(typeof(MvxUITextViewTextTargetBinding), typeof(UITextView), "Text");
}
Alternatively (but maybe not before your immediate demos), you could consider updating to the v3 beta release.
Incidentally, using the 'swiss' binding syntax is generally now the normal way to do this:
this.AddBindings(
new Dictionary<object, string>()
{
{lastname, "Text LastName"},
{uservin, "Text CarVIN"}
});
... and TwoWay binding is the default for most non-Windows MvvmCross bindings.

What must be done to migrate from Storyboard back to XIBs for iOS 4.x support?

I have seen scattered information related to this subject across stackoverflow and other sites, but nothing comprehensive. I've wasted hours upon hours trying to get this to work to no avail. However, I see in this post on stackoverflow (http://stackoverflow.com/questions/7909906/extracting-nibs-xibs-from-storyboards-ios), but nothing with step by step details.
I have only a single view controller (HomeViewController) in my storyboard, and it is extended from a navigation controller.
Can someone please help me identify exactly what all needs to happen to make this work?
Please correct me, add to, etc to make these steps correct:
Create a new XIB (say, HomeViewController.xib)
Copy all items from the storyboard's view controller into this xib
Set the Class type for the view controller to be HomeViewController class.
Hook up all of the IBOutlets and IBActions.
?? - Should I make the xib contain a navigation controller which contains the UIViewController, or should I set the File's Owner to the HomeViewController class??
Add new Window property in the AppDelegate class.
Create a new MainWindow.xib. What setup needs to happen here? (a) File's Owner to UIApplication; (b) Drag new Object to the "Objects" panel and set it's class to my AppDelegate class; (c) Drag new Window to the "Objects" panel; (d) Connect the IBOutlet for the AppDelegate Window property to this new window object.
Go to the project settings (click on the project in the Project Navigation tab, select the target), and clear the "Main Storyboard" field and set the Main Interface to what? HomeViewController? Or Main Window?
Make the change in the AppDelegate class as shown below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
//self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
This is all I know to do, but it still doesn't work. What am I missing?

Resources