Xamarin.Forms - possible to add padding to label using custom renderer? (iOS) - label

Is there a way to add padding to a label using a custom renderer? I know you can cheat by adding a content view around the label and adding padding to the content view; but I want to keep the UI cleaner and not have to add an extra element.
Just to be clear, I don't want a margin - in other words, if I add a background color to the label, you should see padding between the text and the background of the label, like this:

Have you tried something like this:
namespace CustomFinder.iOS.Renderers
{
public class DataLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
if (Control == null)
{
SetNativeControl(new TagUiLabel());
}
base.OnElementChanged(e);
}
}
public class TagUiLabel : UILabel
{
private UIEdgeInsets EdgeInsets { get; set; }
public TagUiLabel()
{
EdgeInsets = new UIEdgeInsets(0, 3, 0, 3);
}
public override void DrawText(CoreGraphics.CGRect rect)
{
base.DrawText(EdgeInsets.InsetRect(rect));
}
}
}
I have this from here
haven't tried it yet.

Related

Measuring textsize depending on style

i need to measure the string width and height depending on some style attributes like font, fontsize and fontattributes (bold/italic). How can i do this?
Thank you!
In Android, it provide the getTextBounds to retrieve the text boundary box and store to bounds. Return in bounds.
https://developer.android.com/reference/android/graphics/Paint#getTextBounds(java.lang.CharSequence,%20int,%20int,%20android.graphics.Rect)
In Xamarin.Forms, we could use Dependency Service to do that.
public interface CalculateTextWidth
{
double calculateWidth(string text);
}
Android Implementation:
[assembly: Dependency(typeof(CalculateTextWidth_Android))]
namespace App6.Droid
{
public class CalculateTextWidth_Android : CalculateTextWidth
{
public double calculateWidth(string text)
{
Rect bounds = new Rect();
TextView textView = new TextView(Forms.Context);
textView.Paint.GetTextBounds(text, 0, text.Length, bounds);
var length = bounds.Width();
return length / Resources.System.DisplayMetrics.ScaledDensity;
}
}
}
Usage:
private void Button_Clicked(object sender, EventArgs e)
{
var s = DependencyService.Get<CalculateTextWidth>().calculateWidth(label.Text);
}
I now use SkiaSharps MeasureText Method.

Xamarin Forms iOS custom renderer for tabbed page

I have tabbed page in Xamarin Forms (iOS side). I need custom renderer for tabbed page - make first tab not scrollable (it could be shown as button or label), rest of tabs should be scrollable.
I think creators of Xamarin Forms tabbed page implemented tabs like a horizontal listview. I just want to put a button as first element on the left and then put that listview with tabs. When button is clicked, the new view is being opened. How to do that?
I am using Naxam Library to provide top tabbed page - this is extension to tabbed page (at iOS it is at bottom). I have tried to use custom renderer, but no breakpoint is hitted. I don's know why.
using CoreAnimation;
using CoreGraphics;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(CustomTabbedPage), typeof(CustomTabbedPageRenderer))]
namespace Layout.xxx.iOS.CustomControlRenderers
{
public class CustomTabbedPageRenderer : Naxam.Controls.Platform.iOS.TopTabbedRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
DependencyService.Get<IAlertHandler>().ShowCustomAlertVoid("", "OnElementChanged");
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
AddButtonToTabbedPage();
DependencyService.Get<IAlertHandler>().ShowCustomAlertVoid("", "ViewDidLoad");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
DependencyService.Get<IAlertHandler>().ShowCustomAlertVoid("", "Dispose");
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
DependencyService.Get<IAlertHandler>().ShowCustomAlertVoid("", "ViewDidAppear");
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
DependencyService.Get<IAlertHandler>().ShowCustomAlertVoid("", "ViewDidDisappear");
}
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
DependencyService.Get<IAlertHandler>().ShowCustomAlertVoid("", "ViewDidLayoutSubviews");
}
public override void DidMoveToParentViewController(UIViewController parent)
{
base.DidMoveToParentViewController(parent);
DependencyService.Get<IAlertHandler>().ShowCustomAlertVoid("", "DidMoveToParentViewController");
}
private void AddButtonToTabbedPage()
{
var btn = new UIButton();
CAGradientLayer btnGradient = new CAGradientLayer();
btnGradient.Frame = btn.Bounds;
btnGradient.Colors = new CGColor[] { Color.Black.ToCGColor(), Color.White.ToCGColor() };
btnGradient.Locations = new NSNumber[] { 0.0f, 0.1f };
btn.Layer.AddSublayer(btnGradient);
btn.Layer.MasksToBounds = true;
btn.Layer.BorderColor = Color.Blue.ToCGColor();
btn.Layer.BorderWidth = 2;
btn.Layer.SetNeedsDisplay();
}
}
}
If you want to fix the first tab, you can use custom renderer to achieve it. Change the index<0 to index<1 to fix the first tab in the method GetPreviousViewController and GetNextViewController:
[assembly: ExportRenderer(typeof(myTopTabbedPage), typeof(myTopTabbedRenderer))]
namespace App12.iOS
{
class myTopTabbedRenderer : TopTabbedRenderer, IUIPageViewControllerDataSource
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
}
public new UIViewController GetPreviousViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
var index = ViewControllers.IndexOf(referenceViewController) - 1;
//in the source, it is if (index < 0) return null;
//change here to if (index < 1) will fix the first tab
if (index < 1) return null;
return ViewControllers[index];
}
public new UIViewController GetNextViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
var index = ViewControllers.IndexOf(referenceViewController) + 1;
//in the source, it is if (index == ViewControllers.Count) return null;
//change here to if (index == ViewControllers.Count || index == 1) will fix the first tab
if (index == ViewControllers.Count || index == 1) return null;
return ViewControllers[index];
}
}
}
And in your xamarin.forms project, use myTopTabbedPage to create tabs:
public class myTopTabbedPage : TopTabbedPage {
}
var tabs = new myTopTabbedPage
{
Title = "TopTabs",
BarBackgroundColor = Color.FromHex("#9C27B0"),
SwipeEnabled = true,
BarIndicatorColor = Color.Green,
BarTextColor = Color.White
};
Try and let me know if it works for you.

CustomRenderer to dynamically change border color

I have an Entry, which I want to add a red border around when a button is pressed if the entry is empty. Therefore I need to be able to change the color dynamically. (standard validator)
xaml:
<local:BorderChange Placeholder="Example Entry" BorderColor="#ff4444"></local:BorderChange>
PCL Control:
namespace Project
{
public class BorderChange : Entry
{
public string BorderColor
{
get;
set;
}
}
}
iOS Customrenderer:
[assembly: ExportRenderer(typeof(BorderChange), typeof(BorderColorChange))]
namespace Project.iOS
{
public class BorderColorChange : EntryRenderer
{
//init color
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control != null)
{
Control.Layer.BorderColor = UIColor.Blue; //This is where i want to add my color
}
}
}
}
How do I pass my property to the CustomRenderer, so that I can change the BorderColor parameter dynamically?
A better approach for such needs is to use Effects.
Here is a well-described example of how to create an effect with parameters as attached properties. You'll be able to bind a property from your view model (Let's say IsValid directly to the attached property of the effect).

How can I create a custom TableView that will allow me to specify footer height and text and space out the text just like in the iOS settings page?

I saw this example:
Xamarin Forms - How to create custom render to give TableSection the default iOS Footer?
It does 75% of what I am looking for with this code:
using CoreGraphics;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(TableView), typeof(Japanese.iOS.TableViewCustomRenderer))]
namespace Japanese.iOS
{
public class TableViewCustomRenderer : TableViewRenderer
{
UITableView tableView;
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
var tableView = Control as UITableView;
var formsTableView = Element as TableView;
tableView.WeakDelegate = new CustomFooterTableViewModelRenderer(formsTableView);
}
void Current_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
}
private class CustomFooterTableViewModelRenderer : TableViewModelRenderer
{
public CustomFooterTableViewModelRenderer(TableView model) : base(model)
{
}
public override nfloat GetHeightForFooter(UITableView tableView, nint section)
{
return 10;
}
public override string TitleForFooter(UITableView tableView, nint section)
{
return "This is the title for this given section";
}
}
}
}
1. However what I would like is to be able to extend TableView so that I am able to put in the XAML some way to set or leave unset the footer text and height. Something like:
<ExtTableView FooterText="abc" FooterHeight="50". ..
2. From experiments with the code above I tried hardcoding in some text and realize that there is no spacing set. So I would also like to find out if there is a way to set the spacing and font so it appears just like in the iOS settings pages?
Could someone suggest how I could go about creating what I am looking for which is I guess something like an ExtTableView class that can accept additional arguments.
As hankide said , I just provide more details.
However what I would like is to be able to extend TableView so that I am able to put in the XAML some way to set or leave unset the footer text and height.
Create MyTableView that inherits from TableView
public class MyTableView : TableView
{
public static readonly BindableProperty FooterHeightProperty =
BindableProperty.Create("FooterHeight", typeof(string), typeof(MyTableView), "");
public string FooterHeight
{
get { return (string)GetValue(FooterHeightProperty); }
set { SetValue(FooterHeightProperty, value); }
}
public static readonly BindableProperty FooterTextProperty =
BindableProperty.Create("FooterText", typeof(string), typeof(MyTableView), "");
public string FooterText
{
get { return (string)GetValue(FooterTextProperty); }
set { SetValue(FooterTextProperty, value); }
}
}
Get the value that you set in XMAL and assign them to CustomFooterTableViewModelRenderer
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
var tableView = Control as UITableView;
var formsTableView = Element as MyTableView;
CustomFooterTableViewModelRenderer render = new CustomFooterTableViewModelRenderer(formsTableView);
render.height = float.Parse(formsTableView.FooterHeight);
render.text = formsTableView.FooterText;
tableView.WeakDelegate = render;
}
private class CustomFooterTableViewModelRenderer : TableViewModelRenderer
{
public float height { get; set; }
public String text { get; set; }
public CustomFooterTableViewModelRenderer(TableView model) : base(model)
{
}
public override UIView GetViewForFooter(UITableView tableView, nint section)
{
UIView view = new UIView(new CGRect(0, 0, tableView.Frame.Width, 50));
view.BackgroundColor = UIColor.Gray;
UILabel label = new UILabel();
label.Frame = new CGRect(0, 0, tableView.Frame.Width, height);
label.BackgroundColor = UIColor.Red;
label.Text = text;
label.Font = UIFont.SystemFontOfSize(15);
view.Add(label);
return view;
}
public override nfloat GetHeightForFooter(UITableView tableView, nint section)
{
return 50;
}
}
Usage:
<local:MyTableView FooterHeight="20" FooterText="ABC">
<TableRoot>
<TableSection>
<TextCell Text="22222" ></TextCell>
</TableSection>
</TableRoot>
</local:MyTableView>
From experiments with the code above I tried hardcoding in some text and realize that there is no spacing set. So I would also like to find out if there is a way to set the spacing and font so it appears just like in the iOS settings pages?
You could override the method GetViewForFooter to change the defalut style of footer,find it in the code above .
My test :
You had the right idea about creating the custom control. Here's what to do:
Create ExtTableView class that inherits from TableView
public class ExtTableView : TableView { }
Create BindableProperties for both FooterText and FooterHeight, as outlined here.
After that you can set the properties in XAML
<ExtTableView FooterText="abc" FooterHeight="50" ...
Within the renderer, you can get the values from Element (which points to our Xamarin.Forms ExtTableView).
// Modify the native control with these values
var text = Element.FooterText;
var height = Element.FooterHeight;

Create a Layout Item for ListView in Xamarin Android

I have a problem and It's 10 days that I am working and can't solve it.I made a layout for each row for ListView.This Layout Contains a linearLayout that there is a TextView and a WebView inside it.Now I Need a C# Project that I can add a new Row to the ListView with new text and url whenever I want.For Example: button.click { ListView.add(Resource.Layout.Items, "Text","Url")}..I know this command is wrong. Just I wanted to clear the problem for you.
I khnow it's custom Row layout and I read manny examples at this site other sites and Xamarin site about that,adapters,... but I can't do it. :(
Please answer me correctly.
It is very important for me.
Thanks a lot.
You need to create an adapter that can work with you custom objects as items. It could look like the following sample:
public class MyAdapter : BaseAdapter<MyItem>
{
readonly LayoutInflater inflater;
List<MyItem> myItemList;
public MyAdapter(Context context)
{
inflater = LayoutInflater.FromContext(context);
myItemList = YOUR_DATASOURCE.GetMyItems();
}
public override MyItem this [int index]
{
get { return myItemList[index]; }
}
public override int Count
{
get { return myItemList.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView ?? inflater.Inflate(Resource.Layout.MyItemLayout, parent, false);
var item = myItemList[position];
var viewHolder = view.Tag as MyViewHolder;
if (viewHolder == null)
{
viewHolder = new MyViewHolder();
viewHolder.Web = view.FindViewById<WebView>(Resource.Id.MyItemLayout_Icon);
viewHolder.Name = view.FindViewById<TextView>(Resource.Id.MyItemLayout_Title);
view.Tag = viewHolder;
}
viewHolder.Web.Url = item.Url; //You need to check how you have to set the url for a WebView
viewHolder.Name.Text = item.Text;
return view;
}
public override void NotifyDataSetChanged()
{
myItemList = YOUR_DATASOURCE.GetMyItems();
base.NotifyDataSetChanged();
}
}
class MyViewHolder : Java.Lang.Object
{
public WebView Web { get; set; }
public TextView Name { get; set; }
}
You apply the adapter to your ListView with ListView.Adapter = new MyAdapter(Activity);. Each time you change an item in you button click event, you tricker (ListView.Adapter as MyAdapter).NotifyDataSetChanged(); which will force the adapter to reload and refresh the data.
YOUR_DATASOURCE represents the point in your code where you store the informations like the url or text of all your items. This could typically be a database or something similar. While GetMyItems() is a method for example to query your database.
Hope this clears things up.

Resources