Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Detect specific device

SebastianKruseSebastianKruse USMember ✭✭✭
edited September 2017 in Xamarin.Forms

Is there any way to detect a specific device (type)? More exactly I want to know if a user uses a iPhone X (or a iOS device with no physical Home Button). That's because I have to apply some specific margins/paddings for these devices and it just look ugly on other devices. Because the "virtual home bar" at the bottom is overlaying some content (yes, I know that this device is not released yet, but I have to fix it for a few Apps so I start early). And of corse for the site if the device is in landscape. Therefore I also need to differ between Top -> Left and Top -> Right Landscape modes. Also I need to differ in C# as well in XAML.

Posts

  • N_BauaN_Baua INMember ✭✭✭✭✭

    Hi @SebastianKruse ,

    Not sure, with specific device, what you mean.
    Xamarin has platform specific conventions and the API is as follows:

    And sometimes I use the following plug-in just to 'identify' the user device:

    Hope it helps.

    --- N Baua

  • SebastianKruseSebastianKruse USMember ✭✭✭

    I mean that I have to identify exactly the iPhone X (because of the Screen) not just iOS. So OnPlatform is not useable here. The DeviceInfoPlugin looks like what I need. I will test this.

  • NMackayNMackay GBInsider, University admin
    edited September 2017

    @SebastianKruse

    What does UIDevice.CurrentDevice.Model return you? (in you iOS project)

  • JohnHardmanJohnHardman GBUniversity admin

    @SebastianKruse - I cannot remember where I got this code from, but I would expect this to provide the information you are after:

    return GetSystemProperty("hw.machine");
    
        public static string GetSystemProperty(string property)
        {
            var pLen = Marshal.AllocHGlobal(sizeof(int));
            sysctlbyname(property, IntPtr.Zero, pLen, IntPtr.Zero, 0);
            var length = Marshal.ReadInt32(pLen);
            var pStr = Marshal.AllocHGlobal(length);
            sysctlbyname(property, pStr, pLen, IntPtr.Zero, 0);
            return Marshal.PtrToStringAnsi(pStr);
        } 
    
  • SebastianKruseSebastianKruse USMember ✭✭✭

    @NMackay said:
    @SebastianKruse

    What does UIDevice.CurrentDevice.Model return you? (in you iOS project)

    It just returns "iPhone" so that is not very useful.

  • N_BauaN_Baua INMember ✭✭✭✭✭

    DevicePlugin Returns the specific version number of the OS such as:

    iOS: 8.1
    Android: 4.4.4
    Windows Phone: 8.10.14219.0
    UWP: 10.0.14393.105

  • JohnHardmanJohnHardman GBUniversity admin

    @SebastianKruse - If it's the hardware you want to know about rather than the o/s version, try the code I posted above.

  • SebastianKruseSebastianKruse USMember ✭✭✭

    @N_Baua said:
    DevicePlugin Returns the specific version number of the OS such as:

    iOS: 8.1
    Android: 4.4.4
    Windows Phone: 8.10.14219.0
    UWP: 10.0.14393.105

    I don't care about the OS. I want to know if a specific device is in use.

    @JohnHardman said:
    @SebastianKruse - If it's the hardware you want to know about rather than the o/s version, try the code I posted above.

    I will try it.

  • SebastianKruseSebastianKruse USMember ✭✭✭

    @JohnHardman what exactly is the "sysctlbyname" call? It is marked as unknown in my project so I guess it is some method you implemented?

  • JohnHardmanJohnHardman GBUniversity admin

    @SebastianKruse - Sorry, I should have mentioned that it's an import from ObjCRuntime.

    So you need to add:

        using ObjCRuntime; 
    

    and then include this in your class:

        [DllImport(Constants.SystemLibrary)] 
        internal static extern int sysctlbyname( 
            [MarshalAs(UnmanagedType.LPStr)] string property, 
            IntPtr output, 
            IntPtr oldLen, 
            IntPtr newp, 
            uint newlen); 
    
  • SebastianKruseSebastianKruse USMember ✭✭✭

    Thanks. But "hw.machine" just returns "x86_x64". Is there a list of all available properties or do you know which one would return something like "iPhone X", "iPad Air" or "iPhone 6S"?

  • SebastianKruseSebastianKruse USMember ✭✭✭

    hmm.. I guess "hw.model" is what I needed but that returns my MacBook instead of the simulator device model. x) But it seems to be that what I need

  • seanydaseanyda GBMember ✭✭✭✭✭

    If the iPhone X has a different screen size (in height) then all the others, Get the height from this code Application.Current.MainPage.Height and do an if statement.

  • SebastianKruseSebastianKruse USMember ✭✭✭

    @seanyda in this case I must first check if the device is in landscape or not. Than it depends on what apple does in the future. If they bring the same "height" to other devices, this would not work anymore and nobody would remember this workaround. So that is a pretty bad design of code.

  • JohnHardmanJohnHardman GBUniversity admin

    @SebastianKruse - Do you have a physical iPhone X to test on?

  • CarlosCMCarlosCM USMember ✭✭

    @SebastianKruse hey! I'm getting good results with this so far. I hope that helps: https://forums.xamarin.com/discussion/comment/301777/#Comment_301777

  • DonCB2BDonCB2B USMember ✭✭✭

    How to know the device is Iphone X?

  • @DonCB2B the best way is to use the UIApplication.SharedApplication.KeyWindow.SafeAreaInsets; within the AppDelegate class. There is an OpenSource project on github which implements this. Take a look at "OpenGeoDB-App" from "Sebastian1989101" (I'm not allowed to post links with my work account yet).

  • DonCB2BDonCB2B USMember ✭✭✭
    edited December 2017

    @JohnHardman said:
    @SebastianKruse - I cannot remember where I got this code from, but I would expect this to provide the information you are after:

    return GetSystemProperty("hw.machine");

        public static string GetSystemProperty(string property)
        {
            var pLen = Marshal.AllocHGlobal(sizeof(int));
            sysctlbyname(property, IntPtr.Zero, pLen, IntPtr.Zero, 0);
            var length = Marshal.ReadInt32(pLen);
            var pStr = Marshal.AllocHGlobal(length);
            sysctlbyname(property, pStr, pLen, IntPtr.Zero, 0);
            return Marshal.PtrToStringAnsi(pStr);
        } 
    

    Confirmed, it works on iphoneX.
    check all iphone version name over here. iPhone X will return string "iphone10,6" and "iphone10,3"
    https://support.hockeyapp.net/kb/client-integration-ios-mac-os-x-tvos/ios-device-types

  • DonCB2BDonCB2B USMember ✭✭✭

    @SebastianKruse.0229 said:
    @DonCB2B the best way is to use the UIApplication.SharedApplication.KeyWindow.SafeAreaInsets; within the AppDelegate class. There is an OpenSource project on github which implements this. Take a look at "OpenGeoDB-App" from "Sebastian1989101" (I'm not allowed to post links with my work account yet).

    will this detect if it is iphoneX?

  • SebastianKruseSebastianKruse USMember ✭✭✭

    @DonCB2B No but it will tell you how the margins should be to show to content in a safe area. You should be careful with the other functions you tested (reading via Marshal the hw.machine property). This could be interfere with the Apple Guidelines because you use not free to use SDK abilities. This could result in a permanent suspension of your account.

  • JohnHardmanJohnHardman GBUniversity admin

    I wrap this sort of thing in

    #if DEBUG
    #endif
    

    to avoid issues when I publish to the App Store.

  • deczalothdeczaloth DEMember ✭✭✭

    @JohnHardman said:
    I wrap this sort of thing in

    #if DEBUG
    #endif

    to avoid issues when I publish to the App Store.

    That is a pitty, i was about to use that piece of code in my App...

    The question remains then. Is there a "safe/legal" way get to know the Device's hardware version? @SebastianKruse ?

    BTW, @JohnHardman i think you got the code from XLabs (or you both took it from the same source, since they have exactly the same routines!)

  • JohnHardmanJohnHardman GBUniversity admin

    @deczaloth said:
    BTW, @JohnHardman i think you got the code from XLabs (or you both took it from the same source, since they have exactly the same routines!)

    Either is possible :-) I normally leave a comment in my code saying where I got code snippets from, but missed the comment off that one.

  • deczalothdeczaloth DEMember ✭✭✭
    edited September 2018

    Well, @JohnHardman @SebastianKruse and others that could come here searching for an answer to this question.

    I made further research and as i explain in an SO thread i found out there should be no problem with the code shared by @JohnHardman.

    Here you can find yet another library that uses that approach (i asked the author and he said that kind of code represented no problem in the Apps that he has seen published in Apple Store).

  • JohnHardmanJohnHardman GBUniversity admin

    @deczaloth

    There are plenty of apps in the App Store that breach the guidelines, but I wouldn't assume that means that all submitted apps that breach the guidelines will get into the store.

    The current guidelines say:
    "2.5.1 Apps may only use public APIs and must run on the currently shipping OS. Learn more about public APIs. Keep your apps up-to-date and make sure you phase out any deprecated features, frameworks or technologies that will no longer be supported in future versions of an OS. Apps should use APIs and frameworks for their intended purposes and indicate that integration in their app description."

    I've just had a quick skim through the public APIs. I may well have skimmed through too quickly and missed the bits used in the code snippet above, but I didn't see them listed as public. If you can see them listed, please let us know. If you cannot see them, I would recommend trying to avoid their use in release builds.

  • JohnHardmanJohnHardman GBUniversity admin

    @JohnHardman said:
    @SebastianKruse - I cannot remember where I got this code from, but I would expect this to provide the information you are after:

    return GetSystemProperty("hw.machine");

        public static string GetSystemProperty(string property)
        {
            var pLen = Marshal.AllocHGlobal(sizeof(int));
            sysctlbyname(property, IntPtr.Zero, pLen, IntPtr.Zero, 0);
            var length = Marshal.ReadInt32(pLen);
            var pStr = Marshal.AllocHGlobal(length);
            sysctlbyname(property, pStr, pLen, IntPtr.Zero, 0);
            return Marshal.PtrToStringAnsi(pStr);
        } 
    

    Just noticed that the code above has a couple of memory leaks so, if using it, replace the return line by:

                var result = Marshal.PtrToStringAnsi(pStr);
                Marshal.FreeHGlobal(pLen);
                Marshal.FreeHGlobal(pStr);
                return result;
    

    I've raised a question with Apple about whether this code is ok to leave in apps being distributed via the App Store. As it turns out, I now have a reason to use this code in my release build, where previously I had only used it in debug builds. Will update here if/when I get a response.

  • LanceMcCarthyLanceMcCarthy USMember ✭✭

    I found that UIApplication.SharedApplication.KeyWindow.SafeAreaInsets is the most reliable. Here's what I did to apply the padding with a Platform Effect that works with any Xamarin.Forms View.

    In iOS project, define the PlatformEffect

    using UIKit;
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.iOS;
    
    [assembly: ResolutionGroupName("YourCompany")]
    [assembly: ExportEffect(typeof(YourApp.iOS.Effects.SafeAreaEffect), "SafeAreaEffect")]
    namespace YourApp.iOS.Effects
    {
        public class SafeAreaEffect : PlatformEffect
        {
            protected override void OnAttached()
            {
                try
                {
                    if (Element is Xamarin.Forms.View view)
                    {
                        var sa = UIApplication.SharedApplication.KeyWindow.SafeAreaInsets;
                        view.Margin = new Thickness(view.Margin.Left, view.Margin.Top, view.Margin.Right, sa.Bottom);
                    }
                }
                catch
                {
                }
            }
    
            protected override void OnDetached()
            {
    
            }
        }
    }
    

    Then, in the Xamarin.Forms project, define the RoutingEffect...

    using Xamarin.Forms;
    
    namespace YourApp.Forms.Effects
    {
        public class SafeAreaEffect : RoutingEffect
        {
            public SafeAreaEffect()
                : base("YourCompany.SafeAreaEffect")
            {
            }
        }
    }
    

    And finally, any View element can get the margin applied by consuming the Effect:

    <ContentPage>
        <Grid>
            <Grid.Effects>
                <effects:SafeAreaEffect/>
            </Grid.Effects>
    
        </Grid>
    </ContentPage>
    
  • KingOfTheNorthKingOfTheNorth Member ✭✭

    Can anyone help me understand why I'm getting an exception error when I hit the line UIApplication.SharedApplication.Delegate.GetWindow().SafeAreaInsets.Bottom? Thanks for the help in advance

    This code is located in the iOS project of a Xamarin Form Solution. I call this methods through an interface in the shared project when the screen is loading.

    public ImageSource GetDisclaimerImageSource()
    {
    if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
    {
    if (UIApplication.SharedApplication.Delegate.GetWindow().SafeAreaInsets.Bottom > 0.0)
    {
    var iphoneX = UIImage.FromBundle("iphoneXBG");
    var iphoneXStream = iphoneX.AsPNG().AsStream();
    return Xamarin.Forms.ImageSource.FromStream(() => { return iphoneXStream; });
    }
    }
    var iphone8 = UIImage.FromBundle("iPhone8BG");
    var iphone8Stream = iphone8.AsPNG().AsStream();
    return Xamarin.Forms.ImageSource.FromStream(() => { return iphone8Stream; });
    }
    }
    }

  • NickKovalskyNickKovalsky USMember ✭✭✭
                        var window = UIApplication.SharedApplication.KeyWindow;
                        if (window != null)
                        {
                            if (window.SafeAreaInsets.Top > 20 || window.SafeAreaInsets.Top == 0)
                                displayInfo = "iPhone X and any other frameless";
                        }
    
  • NickKovalskyNickKovalsky USMember ✭✭✭
    edited September 2019

    The above code stopped working in AppDelegate with recent Forms updates, actually using the below code inside a page renderer in ViewWillAppear:

                var window = UIApplication.SharedApplication.Delegate.GetWindow();
                if (window != null)
                {
                    if (window.SafeAreaInsets.Top > 20 || window.SafeAreaInsets.Top == 0)
                        Display.IsFrameless = true;
                }
    
  • Using the xamarin.essential nuget we can find the exact device model.
    Refer this.
    https://docs.microsoft.com/en-us/xamarin/essentials/device-information?tabs=ios

Sign In or Register to comment.