We are extending our Xamarin.iOS app (by Xamarin.Forms) for CarPlay. When open the CarPlay simulator, the app is showing on the screen of CarPlay, but crashed when launching from CarPlay simulator.
Below is the Info.plist Scene configuration:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UISceneConfigurations</key>
<dict>
<key>CPTemplateApplicationSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>CPTemplateApplicationScene</string>
<key>UISceneConfigurationName</key>
<string>ParkingPlus-Car</string>
<key>UISceneDelegateClassName</key>
<string>ParkingPlus.AppSceneDelegateImp</string>
</dict>
</array>
</dict>
</dict>
"ParkingPlus" is the app bundle name!
The class AppSceneDelegateImp (under the root folder of the iOS project
public class AppSceneDelegateImp : UIResponder, ICPTemplateApplicationSceneDelegate
{
private CPInterfaceController _interfaceController;
public async void DidConnect(CPTemplateApplicationScene templateApplicationScene, CPInterfaceController interfaceController)
{
_interfaceController = interfaceController;
.....
}
public void DidDisconnect(CPTemplateApplicationScene templateApplicationScene, CPInterfaceController interfaceController)
{
_interfaceController.Dispose();
_interfaceController = null;
}
}
When I override the AppDelegate.GetConfiguration as below
public override UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options)
{
...
}
When tapping the app icon on CarPlay, the method will be called. But when I inspected the connectingSceneSession, I found there are some exceptions inside the variable members.
"CPTemplateApplicationSceneSessionRoleApplication has no associated enum value on this platform".
Screenshot for connectingSceneSession inspection
If continue, then the app will throw an exception which seems advise that the SceneDelegate is not being loaded properly:
Exception
My envorinment:
Visual studio for mac Version 8.7.8
Xamarin.iOS 14.0.0
Xcode 12.0
It seems like Xamarin.ios 14 missing something when binding the iOS library. Anyone has the similar issue. Did I do anything wrong or Is there any other way I can implement the CarPlay part feature by Xcode/swift while keeping the mobile app on Xamarin.Forms/Xamarin.iOS?
Appreciate any comments or help.
With the help from Xamarin.ios team, below is the full solution to this issue:
https://github.com/xamarin/xamarin-macios/issues/9749
The AppDelegate to create the scene configuration for two cases (CarPlay and phone)
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
public extern static IntPtr IntPtr_objc_msgSend_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);
public static UISceneConfiguration Create(string? name, NSString sessionRole)
{
global::UIKit.UIApplication.EnsureUIThread();
var nsname = NSString.CreateNative(name);
UISceneConfiguration sceneConfig;
sceneConfig = Runtime.GetNSObject<UISceneConfiguration>(IntPtr_objc_msgSend_IntPtr_IntPtr(Class.GetHandle("UISceneConfiguration"), Selector.GetHandle("configurationWithName:sessionRole:"), nsname, sessionRole.Handle));
NSString.ReleaseNative(nsname);
//Because only the CarPlay scene will be here to create a scene configuration
//We need manually assign the CarPlay scene delegate here!
sceneConfig.DelegateType = typeof(AppCarSceneDelegateImp);
return sceneConfig!;
}
[Export("application:configurationForConnectingSceneSession:options:")]
public UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options)
{
UIWindowSceneSessionRole sessionRole;
bool isCarPlaySceneSession = false;
try
{
//When the connecting scene is a CarPlay scene, an expected exception will be thrown
//Under this moment from Xamarin.iOS.
sessionRole = connectingSceneSession.Role;
}
catch (NotSupportedException ex)
{
if (!string.IsNullOrEmpty(ex.Message) &&
ex.Message.Contains("CPTemplateApplicationSceneSessionRoleApplication"))
{
isCarPlaySceneSession = true;
}
}
if (isCarPlaySceneSession && UIDevice.CurrentDevice.CheckSystemVersion(14,0))
{
return Create("Car", CarPlay.CPTemplateApplicationScene.SessionRoleApplication);
}
else
{
//If it is phone scene, we need the regular UIWindow scene
UISceneConfiguration phoneScene = new UISceneConfiguration("Phone", UIWindowSceneSessionRole.Application);
//And assign the scene delegate here.
phoneScene.DelegateType = typeof(AppWindowSceneDelegateImp);
return phoneScene;
}
}
Create a UIWindowScene delegate to handle the regular mobile scene window
public class AppWindowSceneDelegateImp : UISceneDelegate
{
public override void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
var windowScene = scene as UIWindowScene;
if (windowScene != null)
{
//Assign the Xamarin.iOS app window to this scene
UIApplication.SharedApplication.KeyWindow.WindowScene = windowScene;
UIApplication.SharedApplication.KeyWindow.MakeKeyAndVisible();
}
}
}
Related
I’m new in xamarin ios and I need to add view in existing project. Project use mvvmcross framework.
I’ve done:
Created PerevozkiViewModelClass :MvxViewModel in Core project
Add UI View controller with storydoard (P.S. BaseView extends MvxViewController)
public partial class PerevozkiView : BaseView
{
public PerevozkiView() : base("PerevozkiView", null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
var set = this.CreateBindingSet<PerevozkiView, PerevozkiViewModel>();
set.Apply();
}
}
Delete PerevozkiView.Storyboard
4.Add PerevozkiView.xib and in file owner specify PerevozkiView (I cant choose it from list, so I just hardcode “PerevozkiView as file owner)
After deploy in IOS simulator I’ve got exception:
Foundation.MonoTouchException
Сообщение = Objective-C exception thrown. Name: NSInternalInconsistencyException Reason: -[UIViewController _loadViewFromNibNamed:bundle:] loaded the "PerevozkiView" nib but the view outlet was not set here in Main:
public class Application
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, "AppDelegate");
}
}
I have no idea what is wrong. Pls help
I had an issue in my project and attempted to create a sample project to reproduce it and I was able to.
https://bitbucket.org/theonlylawislove/xamarinnavigationcontrollermemoryleak
The issue is that, when I present a UINavigationController, the navigation controller or its root view controller never gets garbage collected. It DOES work in the iOS simulator though. Why does this memory leak only happen on the device? If you run the sample project on the device, you will never see the Console.WriteLine in the deconstructors called.
I am using XCode5 and Xamarin.iOS 7.0.4.171 (Business Edition)
Here is the AppDelegate I am using to demonstrate the leak.
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
window.RootViewController = new UINavigationController(new RootController ());
window.MakeKeyAndVisible ();
return true;
}
class RootController : UIViewController
{
public RootController ()
{
NavigationItem.RightBarButtonItem = new UIBarButtonItem("Present", UIBarButtonItemStyle.Bordered, (o,e) => {
PresentViewController(new NavigationController(), true, new NSAction(() => {}));
});
}
}
class NavigationController : UINavigationController
{
public NavigationController ()
:base(new TestController())
{
}
~NavigationController()
{
Console.WriteLine("~NavigationController");
}
class TestController : UIViewController
{
~TestController()
{
Console.WriteLine("~TestController");
}
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
Task.Factory.StartNew (() => {
Thread.Sleep(2000);
NSThread.MainThread.InvokeOnMainThread(new NSAction(() => {
DismissViewController(true, new NSAction(() => {
}));
}));
});
}
}
}
}
This is merely a side effect of the conservative collector, there might be some junk on the stack, but using your application will eliminate the junk and allow the object to be released.
If you use SGen, which uses a precise system, you will see the object vanish right away.
EDIT 2: If you're looking for an answer to a similar problem, check Stuart's answer and my comments on it.
EDIT: I am actually getting a Mono.Debugger.Soft.VMDisconnectedException. I also recently installed Windows 8.1 and Resharper (though Resharper is suspended now).
When I access a very simple list property of my view model in my MVVMCross Xamarin iOS application, the program fails. It doesn't quit most of the time: it acts like it's running. The simulator has a black screen and there is no exception. If I breakpoint on if (messagesViewModel != null) source.ItemsSource = messagesViewModel.Messages; and then type messagesViewModel.Messages into the Immediate Window, everything stops, so I can tell it is failing at this line. If instead I "step over", it never moves to the next line.
I was having similar behavior when I was toggling this code in the MvxTableViewSource:
public override int RowsInSection(UITableView tableview, int section)
{
return 1;
}
My view model looks like this:
public class MessagesViewModel : MvxViewModel
{
private List<BaseMessage> _messages = null;
public List<BaseMessage> Messages
{
get
{
return _messages; //yes, I know I'm returning null
//I wasn't at first.
}
}
public MessagesViewModel()
{
}
}
This is my ViewDIdLoad on the MvxTableViewController:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var source = new MessagesTableViewSource(TableView);
//was binding here, removed it for debug purposes
//failure on second line here
var messagesViewModel = ViewModel as MessagesViewModel;
if (messagesViewModel != null) source.ItemsSource = messagesViewModel.Messages;
TableView.Source = source;
TableView.ReloadData();
}
Some initialization code:
public class App : MvxApplication
{
public App()
{
var appStart = new MvxAppStart<MessagesViewModel>();
Mvx.RegisterSingleton<IMvxAppStart>(appStart);
}
}
public partial class AppDelegate : MvxApplicationDelegate
{
//empty functions removed.
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var presenter = new MvxTouchViewPresenter(this, Window);
var setup = new Setup(this, presenter);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
Window.MakeKeyAndVisible();
return true;
}
}
I suspect whatever the error is, it isn't in any of the code you have posted.
I just created a simple ViewModel:
public class FirstViewModel
: MvxViewModel
{
private List<string> _items = new List<string>() { "One", "Two", "Three"};
public List<string> Items
{
get { return _items; }
set { _items = value; RaisePropertyChanged(() => Items); }
}
}
And a simple View:
[Register("FirstView")]
public class FirstView : MvxTableViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
EdgesForExtendedLayout = UIRectEdge.None;
var firstViewModel = ViewModel as FirstViewModel;
var source = new MessagesTableViewSource(TableView);
source.ItemsSource = firstViewModel.Items;
TableView.Source = source;
}
public class MessagesTableViewSource : MvxTableViewSource
{
public MessagesTableViewSource(UITableView tableView) : base(tableView)
{
tableView.RegisterClassForCellReuse(typeof(MessagesCell), new NSString("MessagesCell"));
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
return tableView.DequeueReusableCell("MessagesCell");
}
}
public class MessagesCell : MvxTableViewCell
{
public MessagesCell(IntPtr handle)
: base(handle)
{
var txt = new UILabel(new RectangleF(0, 0, 320, 44));
Add(txt);
this.DelayBind(() =>
{
this.CreateBinding(txt).Apply();
});
}
}
}
And this code runs fine...
I wouldn't completely trust the integration of Xamarin.iOS with the Immediate window - it is better now than it used to be, but I've seen several problems with it before.
Some things to possibly check:
does the above code work for you?
if it does, then what's in your BaseMessage and MessagesTableViewSource classes - perhaps they are causing the problem?
can you use Mvx.Trace("The list is {0}", messagesViewModel.Messages ?? "-null") to view the list? Can you use trace within the ViewModel property get - is it being called? Can you use trace within the ViewModel constructor?
are all your assemblies building against the same versions of things? Are all your assemblies definitely rebuilt? (Check "Build|Configuration Manager")- what version of Xamarin.iOS are you running in VS and in the Mac?
I'm trying to implement Game Center into my game but i have problems with it.
Here is my Main.cs code :
namespace iosgame
{
public class Application
{
[Register ("AppDelegate")]
public partial class AppDelegate : IOSApplication {
MainViewController mainViewController;
public AppDelegate(): base(new Game(new StaticsDatabase(),new StoreDatabase(),new InappPurchase(),new Social(),new MissionsDatabase()), getConfig()) {
}
internal static IOSApplicationConfiguration getConfig() {
IOSApplicationConfiguration config = new IOSApplicationConfiguration();
config.orientationLandscape = true;
config.orientationPortrait = false;
config.useAccelerometer = false;
config.useMonotouchOpenTK = true;
config.useObjectAL = true;
return config;
}
//
// 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)
{
base.FinishedLaunching(app,options);
UIViewController controller = ((IOSApplication)Gdx.app).getUIViewController();
mainViewController = new MainViewController();
controller.View.Add(mainViewController.View);
return true;
}
private bool isGameCenterAPIAvailable()
{
return UIDevice.CurrentDevice.CheckSystemVersion (4, 1);
}
}
static void Main (string[] args)
{
UIApplication.Main (args, null, "AppDelegate");
}
}
}
And here is the superclass of that Main.cs : https://github.com/libgdx/libgdx/blob/master/backends/gdx-backend-iosmonotouch/src/com/badlogic/gdx/backends/ios/IOSApplication.java
I'm trying to use this https://github.com/xamarin/monotouch-samples/blob/master/GameCenterSample/GameCenterSample/MainViewController.cs example but i can't see any authenticate window in my game.I can see "Welcome back ,name" notification but after i log out from gamecenter app and reopen my game but i can't see any authentication window.
How can i fix it?
Thanks in advance
Just call this in FinishedLaunching:
if (!GKLocalPlayer.LocalPlayer.Authenticated) {
GKLocalPlayer.LocalPlayer.Authenticate (error => {
if (error != null)
Console.WriteLine("Error: " + error.LocalizedDescription);
});
}
This should display a Game Center "toast" saying "Welcome back, Player 1".
Here are some ideas if this doesn't work:
Make sure you have setup a new bundle id in the developer portal, and declare it in your Info.plist
Start filling out your app details in iTunes connect (Minimum is description, keywords, icon, 1 screenshot), and make sure to enable Game Center and add your new game to a group
Login with a test iTunes user in Game Center (create in ITC), or the login associated with your developer account
PS - I wouldn't worry about checking for iOS 4.1, just target iOS 5.0 and higher these days.
I have a custom button which inherits from UIButton. I'm handling the TouchUpInside event and want to display a view on top of the current View. Is there such a thing as Dialogs like in Windows development? Or should I do this in another way?
[MonoTouch.Foundation.Register("HRPicker")]
public class HRPicker : UIButton
{
public HRPicker () : base()
{
SetUp();
}
public HRPicker(NSCoder coder) : base(coder)
{
SetUp();
}
public HRPicker(NSObjectFlag t) : base(t)
{
SetUp();
}
public HRPicker(IntPtr handle) : base(handle)
{
SetUp();
}
public HRPicker(RectangleF frame) : base(frame)
{
SetUp();
}
public void SetUp()
{
TouchUpInside += HandleTouchUpInside;
}
void HandleTouchUpInside (object sender, EventArgs e)
{
//I want to display a View here on top of the current one.
}
}
Thanks,
Yes, you have a couple options:
ModalViewController - is called from any UIViewController and overlays a ViewController in the foreground.
UIPopoverController - is a native control that takes a UIViewController and has hooks for presentation and dismissal
WEPopoverController - is a re-implementation of UIPopoverController and allows you to customize the layout, size, and color of the Popover container.
ModalViewController: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
UIPopoverController: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverController_class/Reference/Reference.html
WEPopoverController: https://github.com/mono/monotouch-bindings/tree/master/WEPopover
Update: Regardless of which option you use you must call the presentation of the Popover / Modal view from the main thread:
using(var pool = new NSAutoReleasePool()) {
pool.BeginInvokeOnMainThread(()=>{
// Run your awesome code on the
// main thread here, dawg.
});
}
The equivalent of dialog in Cocoa is UIAlertView: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html
Check out this question for an example of how to use it: Showing an alert with Cocoa
The code should be pretty easy to translate to c# and MonoTouch. But here is a simple example: http://monotouchexamples.com/#19