OzB
OzB

Reputation: 2221

Xamarin Forms image size mismatch

I'm implementing a cross-platform app using Xamarin Forms and I'm struggling with a strange bug: I'm trying to create a button with a text upon it. To achieve it, I'm using AbsoluteLayout.

I've added a image to the iOS project for each iOS resolution types (.png, @2x.png, @3x.png). Everything works fine with standard <Image /> tag. However, when wrapping the image in absolute layout - the Image lose its resolution proportion and it results in borders.

The code above:

    <AbsoluteLayout BackgroundColor="Blue">
        <Image Source="home/donation-button.png"
            AbsoluteLayout.LayoutFlags="All" 
                x:Name="Button"
            AbsoluteLayout.LayoutBounds="0, 0, 1, 1" 
            />
        <Label Text="Hello, World!"
                TextColor="White"
                FontAttributes="Bold"
                FontSize="16"
                AbsoluteLayout.LayoutFlags="All"
                AbsoluteLayout.LayoutBounds="0, 1, 1, 0.5" />
    </AbsoluteLayout> 

Produce the following:

on iPhone 6+/6s+/7+ (the simulator is iPhone 7 Plus): Valid image on iPhone 7 Plus This is the expected behavior.

on iPhone 6/6s/7 (the simulator is iPhone 7): Note the little blue borders on the image Note the little blue borders on the image, which was set as background on AbsoluteLayout

on 5SE/5s and down: Note the blue borders Note the big blue borders.

To debug this, I put the same image twice - firstly in AbsoluteLayout and then as standard item inside the StackLayout. The image in the absolute layout has the proportion bug and the image without it doesn't have.

I'm kinda lost here. There's any way to hack our way through it? I've tried to create. a custom renderer to assign the image size manually, but it seems like UIImage gives different size units then Xamarin uses, and it won't solve the problem on Android.

Any advice will be really appreciated !

Edit (3/5/2017): Just to keep you updated - It seems like this is a general bug in Xamarin Forms. My posted solution is a workaround and I'm stuck at this problem over and over.

For instance, I attempt to create this GUI: The desired GUI image

I've created the code bellow:

<StackLayout Orientation="Horizontal" 
            VerticalOptions="End"
            HorizontalOptions="FillAndExpand"
            BackgroundColor="#dd2c00">
            <Label Text="100"
                FontSize="46"
                Margin="10"
                FontFamily="OpenSans-Bold" 
                TextColor="#ffffff"
                VerticalOptions="FillAndExpand"
                VerticalTextAlignment="Center"
                HorizontalTextAlignment="Center" />
            <Label TextColor="#ffffff"
                FontSize="Medium"
                VerticalOptions="FillAndExpand"
                HorizontalOptions="FillAndExpand"
                VerticalTextAlignment="Center">
                <Label.FormattedText>
                    <FormattedString>
                        <Span FontFamily="OpenSans-Regular" Text="{markup:Localize CandlesMap.FacebookFriendsCandles1}" />
                        <Span FontFamily="OpenSans-Light" Text="{markup:Localize CandlesMap.FacebookFriendsCandles2}" />
                    </FormattedString>
                </Label.FormattedText>
            </Label>
            <Image Source="{markup:ResolvePath map.show_friends_candle_button}" />
        </StackLayout>

Everything is work fine, until I'm adding the image, which results in the following output: The actual GUI

If somebody figures how to hack it out, I'll really appreciate if he, or she, can post the solution here! As long as I get it, it's a problem with iOS UIImage renderer - the UIImage native endpoint is not available when computing the image layout (and therefore the image width and height is -1) and thus XF don't know how to render the image properly.

Upvotes: 13

Views: 5200

Answers (5)

OzB
OzB

Reputation: 2221

Thanks for the suggested answers. Unfortunately, this issue couldn't been resolved using any suggested answer. I gave up on using AbsoluteLayout. Actually, in the past week I ran into many issues regarding images in iOS at RelativeLayout and StackLayout as well. I've reported them to Xamarin.

Meanwhile, I'll post here my workaround, which though could be prettier, is working fine.

using System;
using Xamarin.Forms;
namespace App.Views.Home
{
    public class DonationButton : RelativeLayout
    {
        #region Properties

        /// <summary>
        /// Gets or sets the button text.
        /// </summary>
        /// <value>The text.</value>
        public string Text
        {
            get { return this._textLabel.Text; }
            set { this._textLabel.Text = value; }
        }

        public event EventHandler Clicked;

        #endregion

        #region iVars

        private Image _backgroundImage;
        private Label _textLabel;

        #endregion

        public DonationButton()
        {
            //--------------------------------------------
            //  Setup the background image
            //--------------------------------------------
            this._backgroundImage = new Image()
            {
                Source = ImageSource.FromFile("home__donation_button.png")
            };

            this.Children.Add(this._backgroundImage, (Constraint)null, (Constraint)null, (Constraint)null, (Constraint)null);

            //--------------------------------------------
            //  Add the label
            //--------------------------------------------

            /* See: http://stackoverflow.com/a/40942692/1497516 */
            Func<RelativeLayout, double> getLabelWidth
                = (p) => this._textLabel.Measure(p.Width, p.Height).Request.Width;
            Func<RelativeLayout, double> getLabelHeight
                = (p) => this._textLabel.Measure(p.Width, p.Height).Request.Height;

            this._textLabel = new Label()
            {
                TextColor = Color.White,
                FontAttributes = FontAttributes.Bold,
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
            };

            this.Children.Add(this._textLabel,
                              Constraint.RelativeToParent((parent) => parent.Width - (getLabelWidth(parent) + 10)),
                              Constraint.RelativeToView(this._backgroundImage, (parent, view) => (view.Height - getLabelHeight(parent)) / 2)
                              );

            //--------------------------------------------
            //  Allow clicks
            //--------------------------------------------
            this.GestureRecognizers.Add(new TapGestureRecognizer
            {
                Command = new Command(sender =>
                {
                    if (this.Clicked != null)
                    {
                        this.Clicked(this, EventArgs.Empty);
                    }
                }),
                NumberOfTapsRequired = 1
            });
        }
    }
}

Upvotes: 1

Mohamad Mahmoud Darwish
Mohamad Mahmoud Darwish

Reputation: 4175

try to use Horizontal StackLayout as the following:

<StackLayout BackgroundColor="Blue" Orientation="Horizontal">
        <Image Source="home/donation-button.png"               
            x:Name="Button"
            />
        <Label Text="Hello, World!"
            TextColor="White"
            FontAttributes="Bold"
            FontSize="16"
           />
    </StackLayout >

Upvotes: 0

KARAN VALECHA
KARAN VALECHA

Reputation: 21

Just Add

Aspect:AspectFill

or

Fill

whichever suits your needs!!!

Upvotes: 1

SACn
SACn

Reputation: 1924

Border and resizing are from AbsoluteLayout not from image. See if width of layout can dynamically extend as like 100% from parent container and if it allows options like no-border.

Upvotes: 1

Fabien Mwamba
Fabien Mwamba

Reputation: 91

You can set the Aspect Property of the Image tag to AspectFill which allows your image to fit its container without loosing its resolution`

<AbsoluteLayout BackgroundColor="Blue">
        <Image Source="home/donation-button.png"
            Aspect="AspectFill"
            AbsoluteLayout.LayoutFlags="All" 
            x:Name="Button"
            AbsoluteLayout.LayoutBounds="0, 0, 1, 1"/>
        <Label Text="Hello, World!"
            TextColor="White"
            FontAttributes="Bold"
            FontSize="16"
            AbsoluteLayout.LayoutFlags="All"
            AbsoluteLayout.LayoutBounds="0, 1, 1, 0.5" />
    </AbsoluteLayout>

`

Upvotes: 1

Related Questions