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.

Convert Xamarin.Forms.View to UIView

I need to create a bitmap from a view, like a stack layout for example. Is that possible in iOS? I 've done it in Android already ,using a custom render, but I'm struggling in iOS.
Does anyone have an ideia if that's possible?

Best Answer

  • LeoOsvaldLeoOsvald Member ✭✭
    edited September 2018 Accepted Answer

    Found the solution :

    private void NewElement_OnDrawBitmap(object sender, EventArgs e)
    {
    var formsView = Element;
    var rect = new CGRect(0, 0, 400, 400);
    var iOSView = FormsViewToNativeiOS.ConvertFormsToNative(formsView, rect);
    UiImage = ConvertViewToImage(iOSView);
    SaveImage(UiImage);
    }

    public static UIView ConvertFormsToNative(Xamarin.Forms.View view, CGRect size)
    {
    var renderer = Platform.CreateRenderer(view);

            renderer.NativeView.Frame = size;
    
            renderer.NativeView.AutoresizingMask = UIViewAutoresizing.All;
            renderer.NativeView.ContentMode = UIViewContentMode.ScaleToFill;
    
            renderer.Element.Layout(size.ToRectangle());
    
            var nativeView = renderer.NativeView;
    
            nativeView.SetNeedsLayout();
    
            return nativeView;
        }
    

    void SaveImage(UIImage image)
    {
    var sdCardPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
    string pngFilename = System.IO.Path.Combine(sdCardPath, Element.Nome + DateTime.Now.ToString("dd-MM-yyyy_hh:mm:ss:tt") + ".png");
    NSData imgData = image.AsPNG();
    if (imgData.Save(pngFilename, false, out NSError err))
    {
    Debug.WriteLine("saved as " + pngFilename);
    }
    else
    {
    Debug.WriteLine("NOT saved as " + pngFilename + " because" + err.LocalizedDescription);
    }
    }

Answers

  • BushbertBushbert Member ✭✭✭

    Same principle in IOS. You can use this to generate the bitmap from a UIView.

    public static UIImage AsImage(this UIView view)
    
            {
    
                UIGraphics.BeginImageContextWithOptions(view.Bounds.Size, true, 0);
    
                view.Layer.RenderInContext(UIGraphics.GetCurrentContext());
    
                var img = UIGraphics.GetImageFromCurrentImageContext();
    
                UIGraphics.EndImageContext();
    
                return img;
    
            }
    
  • LeoOsvaldLeoOsvald Member ✭✭

    what view should i pass as parameter? In the android solution, i use the ViewGroup.GetChildAt(desiredindex) so it draws the view i want

  • LandLuLandLu Member, Xamarin Team Xamurai

    Why not use a ContentView and create a custom renderer for it?
    Create a custom contentView and place Forms controls there:

    public partial class CustomView : ContentView
    {
        public CustomView ()
        {
            InitializeComponent ();
        }
    }
    

    And the custom renderer for it:

    [assembly: ExportRenderer(typeof(CustomView), typeof(CustomViewRendererForiOS))]
    namespace Demo.iOS
    {
        public class CustomViewRendererForiOS : ViewRenderer
        {
    
        }
    }
    

    At last you can use this contentView in the page.

  • LeoOsvaldLeoOsvald Member ✭✭

    Im using a render, LandLu, but as i need to convert a xamarin.form.view to a UIView, nothing seems to work as I wish.

    In android im using this:

    private void NewElement_OnDrawBitmap(object sender, EventArgs e)
         {
    
    
            if (ViewGroup != null)
            {
                //gets the desired stack layout
                Android.Views.View subView = ViewGroup.GetChildAt(0);
                int width = subView.Width;
                int height = subView.Height;
                //draws the bitmap
                Bitmap b = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
                Canvas c = new Canvas(b);
                ViewGroup.Draw(c);
    
                //save the file
                SaveBitmapToFile(b);
            }
        }
    
  • LandLuLandLu Member, Xamarin Team Xamurai

    @LeoOsvald ContentView is rendered as UIView on iOS. You can create your custom content view, put your controls there.
    Then the renderer itself is a UIView object.

  • LeoOsvaldLeoOsvald Member ✭✭
    edited September 2018 Accepted Answer

    Found the solution :

    private void NewElement_OnDrawBitmap(object sender, EventArgs e)
    {
    var formsView = Element;
    var rect = new CGRect(0, 0, 400, 400);
    var iOSView = FormsViewToNativeiOS.ConvertFormsToNative(formsView, rect);
    UiImage = ConvertViewToImage(iOSView);
    SaveImage(UiImage);
    }

    public static UIView ConvertFormsToNative(Xamarin.Forms.View view, CGRect size)
    {
    var renderer = Platform.CreateRenderer(view);

            renderer.NativeView.Frame = size;
    
            renderer.NativeView.AutoresizingMask = UIViewAutoresizing.All;
            renderer.NativeView.ContentMode = UIViewContentMode.ScaleToFill;
    
            renderer.Element.Layout(size.ToRectangle());
    
            var nativeView = renderer.NativeView;
    
            nativeView.SetNeedsLayout();
    
            return nativeView;
        }
    

    void SaveImage(UIImage image)
    {
    var sdCardPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
    string pngFilename = System.IO.Path.Combine(sdCardPath, Element.Nome + DateTime.Now.ToString("dd-MM-yyyy_hh:mm:ss:tt") + ".png");
    NSData imgData = image.AsPNG();
    if (imgData.Save(pngFilename, false, out NSError err))
    {
    Debug.WriteLine("saved as " + pngFilename);
    }
    else
    {
    Debug.WriteLine("NOT saved as " + pngFilename + " because" + err.LocalizedDescription);
    }
    }

  • colinedwardscolinedwards USMember ✭✭

    var renderer1 = RendererFactory.GetRenderer(view); <= deprecated
    var renderer2 = Platform.CreateRenderer(view); <= deprecated
    var renderer3 = Platform.CreateRendererWithContext(view, context); <= current

    it would be nice if people made their minds up...

    see Michael Ridland October 2015

    I'm interested to see where the 'context' comes from...

  • colinedwardscolinedwards USMember ✭✭

    updated with obsolescence removed...

    public class NativeButtonRenderer : ButtonRenderer
    {
    public NativeButtonRenderer(Context context) : base(context)
    {
    }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);
    
            if (e.NewElement != null)
            {
                this.Control.Click += Control_Click;
            }
        }
    
        void Control_Click(object sender, EventArgs e)
        {
            var formsView = new CommonFormsView();
    
            var nativeConverted = FormsToNativeDroid.ConvertFormsToNative(formsView, Context, new Rectangle(0, 0, 400, 400));
    
            var builder = new AlertDialog.Builder(Context);
    
            builder.SetView(nativeConverted);
    
            builder.SetTitle("Forms View");
    
            var dialog = builder.Create();
            dialog.Show();
        }
    }
    
    public class FormsToNativeDroid
    {
        public static Android.Views.View ConvertFormsToNative(Xamarin.Forms.View view, Android.Content.Context context, Rectangle size)
        {
            var renderer = Platform.CreateRendererWithContext(view, context);
    
            var viewGroup = renderer.View;
    
            renderer.Tracker.UpdateLayout();
    
            var layoutParams = new ViewGroup.LayoutParams((int)size.Width, (int)size.Height);
            viewGroup.LayoutParameters = layoutParams;
            view.Layout(size);
            viewGroup.Layout(0, 0, (int)view.WidthRequest, (int)view.HeightRequest);
    
            return viewGroup;
        }
    }
    
Sign In or Register to comment.