Maui Handler, Confused at what to put inside of CreatePlatformView iOS - ios

I have a custom view which previously was a Grid but I have changed its class inheritance to a View as this to me seems like the correct thing to do based on what I have read online.
This custom view has content inside of it. (A grid Which has content inside of itself)
This custom View has a handler in the native code.
Then each project has its own version of the handler, where I can handle the mapping methods.
I am adding the content from my custom view to the platform view using a ContentMapper (Inside the native iOS handler)
public static void MapContent(MyHandler handler, MyView view)
{
if (view.Content == null)
return;
handler.PlatformView.AddSubview(view.Content.ToPlatform(handler.MauiContext));
}
And inside of CreatePlatformView() (Native iOS project) I currently have.
UIView uIView = new UIView();
uIView.BackgroundColor = UIColor.Yellow;
return uIView;
But I can't see any of my content, I can however see the yellow background takes up the whole page.
I Have tried doing this inside of the CreatePlatformView()
VirtualView.ToPlatform(VirtualView.Handler.MauiContext) But one it doesn't work and two I don't think that should work anyway.
I could be doing it all wrong I am unsure. If needed I can create a demo project and upload it to GitHub.

Related

How do I use a different view controller for each custom intent for siri shortcuts?

I have prototyped siri shortcuts in my app using custom intents successfully. I would like to define custom UI for different shortcut types. For example IntentA would show a tall card with lots of labels and IntentB would show a short card with an image and one label.
I don't see any direct link in the code between the IntentViewController used in the MainInterface storyboard and the intent definition file.
If possible, I would like to have IntentAViewController and IntentBViewController defined in the MainInterface storyboard and handle them accordingly, but I don't see where I would tell the extension which viewcontroller or storyboard id to load for each intent.
If not possible, then what is the best practice for accomplishing multiple intent UIs? (I haven't found any tutorials that had more than one intent).
I found the solution inside the SoupChef example app from Apple. Since there is only one main IntentViewController and one MainInterface storyboard, using intents you should detect the intent type and add the necessary view controller as the child of the IntentViewController.
From IntentViewController in SoupChef:
/* Different UIs can be displayed depending if the intent is in the confirmation phase or the handle phase.
This example uses view controller containment to manage each of the different views via a dedicated view controller.
*/
if interaction.intentHandlingStatus == .ready {
let viewController = InvoiceViewController(for: intent)
attachChild(viewController)
completion(true, parameters, desiredSize)
} else if interaction.intentHandlingStatus == .success {
if let response = interaction.intentResponse as? OrderSoupIntentResponse {
let viewController = OrderConfirmedViewController(for: intent, with: response)
attachChild(viewController)
completion(true, parameters, desiredSize)
}
}
(where attach child calls addChild, addSubview, didMove and sets up constraints)

How Xamarin.Forms adjusts iOS/Android native elements (auto)height/width?

This is a general question and specific one.
For general purpose - to understand how XF works and specifically - for using in my custom controls.
So, the thing is that I should put the elements (generated by XF for specific system (iOS or Android)) into some custom place within custom renderer.
In a nutshell, it's embedding XF controls into native interface, in more details I need to put the controls into custom alert/popup.
The issue is that the final size of the elements (views) can be different from initially generated XF ones.
So I need further adjustment of them (for instance, to fit parent view bounds (as again, the parent size is custom one and usually platform-dependent and screen-size-dependent)).
Let's say, I have:
<controls:AlertPanel
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
where AlertPanel is:
public class AlertPanel : Frame
inside of the custom renderer (of the AlertPanel for iOS):
// container is my native (manually created) view
container.AddSubview(this);
and this is the renderer class (inherited basically from UIView).
But if I do this right on IsVisible attribute setting to true (and the whole UI control block is dynamically created (or at least positioned)), the bounds setup via:
this.Frame = new CGRect(...);
... doesn't quite properly reflect what I expect.
It's much better when I apply that after (for instance) 10 ms delay, however, this is neither nice approach, nor having clear understanding of which way this all works.
In addition, I haven't found any constrains applied to the view generated by XF, so I am considering the bounds are strictly defined or calculated dynamically.
Any clues?

How to hint where to show a view in MvvmCross with SplitView

My MvvmCross app uses a custom presenter that creates a SplitView when run on an Ipad. Both master and detail contain a navigation controller. This works fine except that I don't know how to hint the system where I want the next view to show.
I have a couple of views that sometimes should be shown in the detail view and sometimes in the master. If run on an iPhone they will be shown in the single navigation controller.
So in the ViewModel I would like to hint where to put the next view. Something like
ShowViewModel(paramObject, ShowInMaster);
If run on an iPhone the ShowInMaster will be ignored.
Is this possible or am I perhaps doing this all wrong?
There's an optional presentationBundle parameter you can use in most of the ShowViewModel overrides - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNavigatingObject.cs#L39
You can create a bundle simply from a Dictionary<string,string>() - e.g. you could use new MvxBundle(new Dictionary<string,string>() { { "ShowSplit":"true" } })
When used, this presentation bundle will get placed into the MvxViewModelRequest - in the public IDictionary<string, string> PresentationValues { get; set; } member - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxViewModelRequest.cs#L33
The request will then get passed to your UI presenter (aka the 'navigation service' in other frameworks) - and your custom code in the presenters on each platform can then decide what to do with these 'presentation' hints - e.g. it can override public override void Show(MvxViewModelRequest request) to inspect the presentation hint contents and to then do some custom split view display (see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Touch/Views/Presenters/MvxTouchViewPresenter.cs#L45 for the default behaviour)
If it helps, a simple split view display (using fixed logic rather than presentation hints) is in N=24 of http://mvvmcross.blogspot.com

Global hook for showing UIViews

I have an iOS app I'm working on using Xamarin and MVVMCross, but I am also using a third-party native library which includes some views of it's own (loaded from .xib files with the implementation in the library). What I need to do is set some properties on those native views and I'm trying to see if there's a way to do it that doesn't involve jumping into xcode and trying to recompile that whole thing (because I can't get that working at the moment).
So my question is, is there a way to intercept, application-wide, all attempts to load a view so that I can examine the view and if it's one of those from the third-party library, set some properties on it before it's displayed?
MvvmCross has a MvxTouchViewPresenter which has a ChangePresentation property, but it seems to only apply to MvxViewController loaded by MvvmCross itself.
You can very easily intercept all attempts to access a viewmodel by overriding the Show() method on your MvxTouchPresenter. For example:
public override void Show(MvxViewModelRequest request)
{
IMvxTouchView view = this.CreateViewControllerFor(request);
UIViewController viewController = (UIViewController) view;
this.Show(view);
}
You can then examine all Views in the UIView heirarchy by using something similar to the Objective-C code in this post. You just need to walk through all the UIViews in the viewController property and identify your view (perhaps by "smelling it" with respondsToSelector; I can't figure out exactly how you'd use isKindOfClass if Xamarin doesn't know it).
I hope I understood your question. Let me know if there's anything else missing.

How are NSTableCellViews supposed to be laid out?

I have a fairly basic MainWindow.xib with a source list-style sidebar. I created it by dragging the Source List template into the window, which already contains two NSTableCellViews: HeaderCell and DataCell.
The latter consists of an icon (using NSImageView) and a label (NSTextField). Instead, I want the label and another, smaller label underneath. In IB, this looks as follows:
If I focus on just DataCell, it highlights accordingly:
Thing is, actually running the program, it looks nothing like the template:
Notice how the two NSTextFields just get smashed together into one. My understanding was that view-based NSOutlineViews (and view-based NSTableViews, for that matter) are supposed to be designed as a template from within IB. Instead, the dimensions from the template seem to get mostly ignored.
Here's the code that sets the view's values from the data source:
public class TourSourceListDelegate : NSOutlineViewDelegate
{
public override bool IsGroupItem(NSOutlineView outlineView, MonoMac.Foundation.NSObject item)
{
return (item as TourSourceListDataSource.Item).IsHeader;
}
public override NSView GetView(NSOutlineView outlineView, NSTableColumn tableColumn, MonoMac.Foundation.NSObject item)
{
if (IsGroupItem(outlineView, item))
{
return outlineView.MakeView("HeaderCell", this);
}
else
{
var data = item as TourSourceListDataSource.Item;
var dataView = outlineView.MakeView("DataCell", this);
(dataView.Subviews[0] as NSTextField).StringValue = data.Name;
(dataView.Subviews[1] as NSTextField).StringValue = data.Date_start.ToShortDateString();
return dataView;
}
}
}
I've tried overriding GetRowHeight, but that doesn't seem to resolve the problem (it makes more room, but still doesn't let the views distribute themselves properly), nor does it seem necessary.
I've also tried playing with the various Autosizing, Autoresizes Subviews, etc. toggles in IB, but that doesn't seem to produce intuitive results, and again, it doesn't seem necessary — the view as presented in IB is exactly what I want, just with slightly longer labels in practice.
I haven't tried converting this to AutoLayout yet.
What obvious step am I missing?
Some more info that probably doesn't make a difference: this is a Xamarin.Mac/MonoMac project with Xcode 5.0, MacOSX10.8.sdk, Xamarin Studio 4.0.12, Xamarin.Mac 4.0.12, and Mono 3.2.3 (targeting Mono / .NET 4.0). I've also enabled App Sandboxing.
What's important in interface builder is the view hierarchy. What kind of view is that cell? Are those labels really subviews of the cellview or not? The hierarchy should look something like:
One thing that's fishy that I see is accessing dataView.Subviews[0] and [1]. If you're adding subviews to your cells then should be creating your own NSTableViewCell subclasses, with each view connecting to the subclass' IBOutlet properties. The subclass doesn't need any code in its implementation, just the declaration of its properties in #interface, such as titleField and descriptionField, and an empty #implementation that auto-synthesizes them.
Then makeViewWithIdentifier (or apprently the glue MakeView in Xamarin) when passed the right identifier should create your NSTableViewCell subclass, and at runtime you can verify that using po dataView in the debugger. Then you access the subviews using the properties of your NSTableViewCell subclass' interface instead of assuming which view is in which position with the subview array, using dataView.titleField and dataView.descriptionField.
If your cell view has one text field then you can use NSTableViewCell without subclassing, but do connect up the textField outlet (its connected by default as long as you don't delete & recreate the cell view's label view) so you can access it through the property, again instead of having to dive into the subviews array.
All that said, it's not really clear why you're seeing what you are. It looks like those aren't the subviews you expect, and might even look like the wrong fonts as well as in the wrong positions. Using a custom subclass of NSTableViewCell and verifying its class at runtime is a good way of making sure it's creating the view you expect, but you can also dump the subview within the debugger using po [dataView _subtreeDescription].

Resources