Enable and control iOS 11 Large Titles in Xamarin.Forms

Apple introduced with iOS 11 a new UI in particular for larger titles: if you open your email, you see at the top a large title. That sit in the Navigation Bar and can be scrolled away when more space is needed. We can enable large titles with a renderer.

For this example, I only created a new project and added the following renderer in the iOS project under Renderers folder. You can easily add to all your solution the following renderer in your iOS project:

using System;
using YourProject.iOS.Renderers;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(NavigationPage), 
                          typeof(CustomNavigationRenderer))]
namespace YourProject.iOS.Renderers
{
    public class CustomNavigationRenderer : NavigationRenderer
    {
        protected override void OnElementChanged(
                                VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            // Enables iOS 11 new Large Titles system-wide
            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
            {
                // Enable Large Titles
                NavigationBar.PrefersLargeTitles = true;

                // Adopt same TextAttributes as for normal titles
                // beacuse Xamarin.Forms NavigationPage
                // does not have a dedicated property for this yet
                UINavigationBar.Appearance.LargeTitleTextAttributes =
                    new UIStringAttributes { 
                            ForegroundColor = 
                               (Element as NavigationPage)?.
                               BarTextColor.ToUIColor() };
            }
        }
    }
}

iOS 8 Screenshot - Large title

Happy coding!

Xamarin, no show images in the device but only in the simulator

In my project I have some images to display. The funny thing is I can see images in the simulator but not in a real device.

Simulator

Simulator-Screen Shot

iPhone (real device)

iPhone-Screenshot

Solution

iPhone is case-sensitive and the name of your images must be specified correctly. iOS Simulator probably is not case-sensitive and then it displays images with every name.

An error occurs with MobileCenter for Xamarin iOS

I added Microsoft Mobile Center to my project after creating the app there. On MobileCenter documentation you can know the Install Identifier for your application (MobileCenter documentation is here).

System.Guid installId = MobileCenter.InstallId;

This function is working fine if you have Android or iOS 10. With iOS less than 10 an error occurs:

Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).

and the StackTrace is similar to

at System.Guid+GuidResult.SetFailure (System.Guid+ParseFailureKind failure, System.String failureMessageID, System.Object failureMessageFormatArgument, System.String failureArgumentName, System.Exception innerException) [0x00030] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:198 \n at System.Guid+GuidResult.SetFailure (System.Guid+ParseFailureKind failure, System.String failureMessageID) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:184 \n at System.Guid.TryParseGuidWithDashes (System.String guidString, System.Guid+GuidResult& result) [0x0008f] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:695 \n at System.Guid.TryParseGuid (System.String g, System.Guid+GuidStyles flags, System.Guid+GuidResult& result) [0x00115] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:443 \n at System.Guid.Parse (System.String input) [0x00021] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:262 \n at Microsoft.Azure.Mobile.MobileCenter.get_InstallId () [0x0000a] in :0 \n at myInventories.Helpers.LogHelpers.SendMessageToAzure (System.String EventName, System.String PageName, System.String BaseClass, myInventories.Logs.ActionType Action, System.String MoreInfo, System.Collections.Generic.Dictionary`2[TKey,TValue] DictionaryInfo) [0x00008] in /Users/enricorossini/Projects/myInventories/myInventories/myInventories/Helpers/LogHelpers.cs:72

To avoid this error you have to check the OS version.

Implementation

In your solution you have to add in all projects Device Information Plugin (for NuGet Xam.Plugin.DeviceInfo). Then you check easily the OS version with

// check the OS version to avoid error on MobileCenter
string MobileId = "";
var osInfo = CrossDeviceInfo.Current;
if ((osInfo.Platform == Plugin.DeviceInfo.Abstractions.Platform.iOS) && 
    (osInfo.VersionNumber.Major < 10)) 
{
    MobileId = "Unknown";
}
else {
    MobileId = MobileCenter.InstallId.ToString();
}

Happy coding!

Xamarin Forms and Google Mobile Ads for iOS: update

In my previous post I explaind how to add on your application advertising. In those days Xamarin has removed from the Component Store the component called Google Mobile Ads for iOS.

Now you have to install another component called Firebase AdMob for iOS.

Firebase_admob_ios

After installed this component you can see in your Output window a similar info without see an advert in your app:

2016-11-18 11:28:14.853 WordBankEasy.iOS[11103:2250070] <Google> You must set the rootViewController property of <GADBannerView: 0x1034702c0; frame = (-10 0; 320 50); clipsToBounds = YES; layer = <CALayer: 0x174e39d40>> before loading a request.

To fix this issue you have to find before the right UIViewController. For that you can use the following code:

    UIViewController viewController = null;
    foreach (UIWindow rootView in UIApplication.SharedApplication.Windows) {
        if (rootView != null) {
            viewController = rootView.RootViewController;
        }
    }

    adView = new BannerView(size: AdSizeCons.Banner,
                            origin: new CGPoint(-10, 0))
    {
        AdUnitID = AdmobID,
        RootViewController = viewController
    };

Happy coding!

“System.IO.FileNotFoundException” using controls from another assembly in Xamarin Forms on iOS.

I was building a Xamarin solution with my components like that:

ProjectList

All my components (PSC.Xamarin.Controls.*) are working fine in other solutions.Always fine for Windows or UWP solutions. A problem was born when I started to deployed my solutions on iOS. When on the app I opened a page with my controls I always received an error like:

FileNotFoundException

System.IO.FileNotFoundException: Could not load file or assembly 'PSC.Xamarin.Controls.BindablePicker' or one of its dependencies. The system cannot find the file specified.

I can check my references and deployment settings in all ways, it turns out there’s probably a bug in Xamarin Forms’ iOS implementation. If you use controls and only controls from a PCL or dll that does not contain other code that is called from the app, apparently Xamarin on iOS ‘forgets’ to load/deploy the assembly (or something like that). But there is a work around.

The work around is as simple as it is weird. You go the iOS app’s AppDelegate class, and you add a reference to your class(es). If this is not enough, in the FinishedLaunching function you have to add a new instance to your control as in the following example:

using System;
using System.Collections.Generic;
using System.Linq;
using PSC.Xamarin.Controls.BindablePicker;

using Foundation;
using UIKit;

namespace myInventories.iOS {
    [Register("AppDelegate")]
    public partial class AppDelegate : 
        global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate {
        public override bool FinishedLaunching(UIApplication app, 
            NSDictionary options) {
            global::Xamarin.Forms.Forms.Init();

            // initialize my components
            // this is a work around only for iOS. 
            // If you don't do that, you received an error like 
            // System.IO.FileNotFoundException
            BindablePicker temporary = new BindablePicker();

            Xamarin.FormsMaps.Init();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}

Happy coding!

Xamarin Forms and Google Mobile Ads for iOS

If you have a new Xamarin Project and you want to add an advertising, the simple way it is to use Google Admob. This implementation is only for Android and iOS.

One of the first things people think about when developing for a new platform / using a new technology is monetization; and in my case the question is: how easy is it to integrate AdMob? For Xamarin Forms the answer would be: “It depends” – it depends on luck & on the complexity of what you want to achieve; but I will detail this as we move along.

The first thing you need to do is add the required components to your projects. For this walktrough I will be using Visual Studio but it should be relatively the same when using Xamarin Studio. Here, things go separate ways for each of the platforms:

  • for Android – add the Google Play Services component
  • for iOS – add the AdMob component
  • for Windows Phone – download the SDK from here and add it as a reference

By now, you Android project should no longer be building & you should be receiving a COMPILETODALVIK : UNEXPECTED TOP-LEVEL error. To fix that, go into your Droid project properties, select the Android Options tab and then under Advanced modify the value for the Java Max Heap Size to 1G. Your project should now build without any errors.

Next, inside your shared / PCL project add a new Content View and call it AdMobView. Remove the code generated inside it’s constructor & it should look like this:

public class AdMobView : ContentView
{
    public AdMobView() { }
}

Add this new view to your page. In XAML you can do it like this:

<controls:AdMobView WidthRequest="320" HeightRequest="50" />

Make sure NOTHING interferes with the control. By nothing I mean – overlapping controls, page padding, control margins / spacing, etc. If you have something overlapping the ad control, ads will not display & you won’t receive an error, so be careful.

Android

Add a new class called AdMobRenderer with the code below. Make sure to keep the ExportRenderer attribute above the namespace, otherwise the magic won’t happen.

using WordBankEasy.Droid.Renderers;
using WordBankEasy.Views.AdMob;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(AdMobView), typeof(AdMobRenderer))]

namespace WordBankEasy.Droid.Renderers {
    public class AdMobRenderer : ViewRenderer<AdMobView, Android.Gms.Ads.AdView> {
        protected override void OnElementChanged(ElementChangedEventArgs<AdMobView> e) {
            base.OnElementChanged(e);

            if (Control == null) {
                var ad = new Android.Gms.Ads.AdView(Forms.Context);
                ad.AdSize = Android.Gms.Ads.AdSize.Banner;
                ad.AdUnitId = "ca-app-pub-4381168884554284/2250461656";

                var requestbuilder = new Android.Gms.Ads.AdRequest.Builder();
                ad.LoadAd(requestbuilder.Build());

                SetNativeControl(ad);
            }
        }
    }
}

Next, you need to modify your AndroidManifest.xml file to add the AdActivity & required permissions for displaying ads: ACCESS_NETWORK_STATE, INTERNET; just like in the example below (see also http://puresourcecode.com/dotnet/post/Android-required-permissions).

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-sdk android:minSdkVersion="15" />
    <application>
    <activity android:name="com.google.android.gms.ads.AdActivity" 
            android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" android:theme="@android:style/Theme.Translucent" />
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

That’s it. Your Android build should now display ads inside the AdMobView content view.

iOS

This, I haven’t got the chance to test yet as I don’t have a Mac around but people say it works, so I’ve added it for reference. Same as before, just add a new class called AdMobRenderer and copy-paste the code below but before you have to add a component.

In your iOS project click on “Components” (as in the picture)

Components

and click on “Get More Components…”. Then search admob and install it.

Google-Admob-Xamarin

using Xamarin.Forms;
using CoreGraphics;
using Xamarin.Forms.Platform.iOS;
using UIKit;
using WordBankEasy.Views.AdMob;
using WordBankEasy.iOS.Renderers;
using Google.MobileAds;

[assembly: ExportRenderer(typeof(AdMobView), typeof(AdMobRenderer))]
namespace WordBankEasy.iOS.Renderers {
    public class AdMobRenderer : ViewRenderer {
        const string AdmobID = "ca-app-pub-4381168884554284/5843056458";

        BannerView adView;
        bool viewOnScreen;

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e) {
            base.OnElementChanged(e);

            if (e.NewElement == null)
                return;

            if (e.OldElement == null) {
                adView = new BannerView(size: AdSizeCons.Banner, origin: new CGPoint(-10, 0)) {
                    AdUnitID = AdmobID,
                    RootViewController = UIApplication.SharedApplication.Windows[0].RootViewController
                };

                adView.AdReceived += (sender, args) => {
                    if (!viewOnScreen) this.AddSubview(adView);
                    viewOnScreen = true;
                };

                adView.LoadRequest(Request.GetDefaultRequest());
                base.SetNativeControl(adView);
            }
        }
    }
}

Here you can receive an strange error like:

Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInvalidArgumentException Reason: -[AppDelegate window]: unrecognized selector sent to instance 0x7ffee9cdd4d0

Native stack trace:

0 CoreFoundation 0x000000010a12ed85 __exceptionPreprocess + 165

1 libobjc.A.dylib 0x000000010a5e3deb objc_exception_throw + 48

2 CoreFoundation 0x000000010a137d3d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205

3 CoreFoundation 0x000000010a07db17 forwarding + 487

4 CoreFoundation 0x000000010a07d8a8 _CF_forwarding_prep_0 + 120

5 WordBankEasyiOS 0x0000000100bc65e4 GADiTunesMetadataForFileAtPath + 4181

6 WordBankEasyiOS 0x0000000100c2d484 hasRequiredParams + 10260

7 WordBankEasyiOS 0x0000000100bc5f06 GADiTunesMetadataForFileAtPath + 2423

8 WordBankEasyiOS 0x0000000100bc1c35 GADCategories_NSURL_GADNSURLUtilities + 4438

9 WordBankEasyiOS 0x0000000100c00fde GADDispatchAsyncSafeMainQueue + 45

10 libobjc.A.dylib 0x000000010a5e4bff _class_initialize + 679

11 libobjc.A.dylib 0x000000010a5eacc5 lookUpImpOrForward + 176

12 libobjc.A.dylib 0x000000010a5f98bb objc_msgSend + 187

13 WordBankEasyiOS 0x0000000100bbe0ff GADCategories_DFPBannerView_CustomRenderedAd + 18074

14 WordBankEasyiOS 0x0000000100bbe382 GADCategories_DFPBannerView_CustomRenderedAd + 18717

15 WordBankEasyiOS 0x0000000100bbe527 GADCategories_DFPBannerView_CustomRenderedAd + 19138

16 ??? 0x000000011d368a0e 0x0 + 4785080846

17 ??? 0x000000011d36843b 0x0 + 4785079355

18 ??? 0x000000011d364d88 0x0 + 4785065352

19 ??? 0x000000011c51bbd6 0x0 + 4770085846

20 ??? 0x000000011c51b138 0x0 + 4770083128

21 ??? 0x000000011c51b138 0x0 + 4770083128

22 ??? 0x000000011c51b138 0x0 + 4770083128

Don't worry! I received the same error and I spend a lot of time to understand and fix it. There is a workaround that seems working fine.

In your iOS project, open AppDelegate.cs and add this code:

/// <summary>
/// Gets the window.
/// </summary>
/// <returns>UIWindow.</returns>
[Export("window")]
public UIWindow GetWindow() {
   return UIApplication.SharedApplication.Windows[0];
}

A complete example of this class is:

using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using ImageCircle.Forms.Plugin.iOS;
using UIKit;

namespace WordBankEasy.iOS {
    // 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 : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate {
        //
        // 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) {
            global::Xamarin.Forms.Forms.Init();

            ImageCircleRenderer.Init();

            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }

        /// <summary>
        /// Gets the window.
        /// </summary>
        /// <returns>UIWindow.</returns>
        [Export("window")]
        public UIWindow GetWindow() {
            return UIApplication.SharedApplication.Windows[0];
        }
    }
}

After that you can start you app and see you advertising without problem. For now :)

Happy coding!

Write C#. Run JavaScript.

BridgeNet

Open Source C# to JavaScript Compiler and Frameworks. Run Your App On Any Device Using JavaScript.

Use Bridge.NET to build platform independent applications for mobile, web and desktop. Run on iOS, Windows, Mac, Linux and billions of other devices with JavaScript support. Compile your C# code into native JavaScript and deploy on Billions of devices.

Try it on bridge.net or fork it on GitHub

Advertsing

125X125_06

Calendar

<<  October 2017  >>
MonTueWedThuFriSatSun
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar

Tag cloud

Month List