Xamarin iOS styling on overriding on a UIButton - xamarin

I am creating an iOS application (not Xamarin Forms) in Xamarin Studio and I have a question regarding styling. In this particular case I am setting the default appearance of a UIButton using the iOS Appearance API and calling this method from the FinishedLaunching method in the AppDelegate class.
private void SetUserInterfaceStyles()
{
//UIButton
UIButton.Appearance.TintColor = UIColor.FromRGB(73, 217, 41);
UIButton.Appearance.BackgroundColor = UIColor.FromRGB(73, 217, 41);
UIButton.Appearance.SetTitleColor(UIColor.White, UIControlState.Normal);
}
Now this works fine but there particular cases where the styling of the button differs from the default size and when setting any of the colours in the storyboard using Xamarin iOS designer the settings are not applied and the defaults from the SetUserInterfaceStyles method are used. Setting the colour in the ViewDidLoad of the ViewController does override the default but I would prefer a designer solution.
Is there a way to have multiple styles defined for a button or a working method to override the default styling in the form designer?

Looks like the recommended method of accomplishing this is not to use the Appearance API but to sub class the UI Button class and then to assign that class to the Storyboard button.
Something like this...
public StandardButton(IntPtr handle) : base (handle)
{
this.TouchDown += (sender, e) => { SetButtonStyleOnClick(); };
this.TouchUpInside += (sender, e) => { SetButtonStyleOnClickRelease(); };
this.SetBackgroundImage(new UIImage().FromColour(ApplicationStyles.ButtonBackgroundColourEnabled), UIControlState.Normal);
this.SetBackgroundImage(new UIImage().FromColour(ApplicationStyles.ButtonBackgroundColourDisabled), UIControlState.Disabled);
this.Layer.CornerRadius = ApplicationStyles.StandardButtonCornerRadius;
this.ClipsToBounds = true;
this.TintColor = ApplicationStyles.ButtonBackgroundColourEnabled;
this.SetTitleColor(ApplicationStyles.ButtonTitleColourEnabled, UIControlState.Normal);
this.SetTitleColor(ApplicationStyles.ButtonTitleColourDisabled, UIControlState.Disabled);
}
private void SetButtonStyleOnClick()
{
var shrink = CGAffineTransform.MakeScale(0.95f, 0.95f);
this.Transform = shrink;
this.BackgroundColor = ApplicationStyles.ButtonBackgroundColourClicked;
UIView.BeginAnimations("shrink", this.Handle);
UIView.SetAnimationDuration(0.25);
UIView.SetAnimationDelegate(this);
UIView.CommitAnimations();
}
private void SetButtonStyleOnClickRelease()
{
CGAffineTransform pop = CGAffineTransform.MakeScale(1.0f, 1.0f);
this.Transform = pop;
this.BackgroundColor = ApplicationStyles.ButtonBackgroundColourEnabled;
UIView.BeginAnimations("pop", this.Handle);
UIView.SetAnimationDuration(0.25);
UIView.SetAnimationDelegate(this);
UIView.CommitAnimations();
}

Related

FontAwesome icon with normal text in Xamarin

I want to make a button that has a small icon (from FontAwesome) and text on it in my Xamarin app. I know I can just make a button but the problem is that I will require two fonts (the standard font for text and FontAwesome for the icon). Would anyone happen to know how I can do this, or if there is another way to achieve what I want?
Thanks!
As the json mentioned, I just made a simple implementation.
Create a new class inherit from Label, set FormattedText to combine the string(standard and icon), and add tap gesture on it .
public class MyLabel : Label
{
public delegate void MyHandler(object sender, EventArgs e);
public event MyHandler myEvent;
public MyLabel(string _myIcon, string _myText)
{
//build the UI
FormattedString text = new FormattedString();
text.Spans.Add(new Span { Text = _myIcon ,FontFamily= "FontAwesome5Free-Regular" });
text.Spans.Add(new Span { Text = _myText, TextColor = Color.Red ,BackgroundColor = Color.Blue });
FormattedText = text;
//tap event
TapGestureRecognizer tap = new TapGestureRecognizer();
tap.Tapped += (sender,e) => {
myEvent(sender,e);
};
}
}
Usage
MyLabel label = new MyLabel("", "test");
label.myEvent += (sener,e) =>
{
//do something when tapping
};
Content = label;
For how to integrate FontAwesome in Xamarin.Forms ,refer to
https://montemagno.com/xamarin-forms-custom-fonts-everywhere/.

Xamarin memory leakage

I'm a newbie to Xamarin. I have created an application that uses DrawerLayout(Android). But my problem is that every time i select a item in the menu(DrawerLayout menu), the memory increases, and this causes the app to become slow and crush. I've tried to use Xamarin profiler to analyze memory leaks - it suspects something called String.FastAllocationString, but it doesn't really show the line(code) that causes String.FastAllocationString issue. Please help ? Here is my code :
MainActivity
DrawerLayout drawerLayout;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
drawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawer_layout);
// Init toolbar
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.app_bar);
SetSupportActionBar(toolbar);
SupportActionBar.SetTitle(Resource.String.app_name);
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetDisplayShowHomeEnabled(true);
// Attach item selected handler to navigation view
var navigationView = FindViewById<NavigationView>(Resource.Id.nav_view);
navigationView.NavigationItemSelected += NavigationView_NavigationItemSelected;
// Create ActionBarDrawerToggle button and add it to the toolbar
var drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, Resource.String.open_drawer, Resource.String.close_drawer);
drawerLayout.SetDrawerListener(drawerToggle);
drawerToggle.SyncState();
}
void NavigationView_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e)
{
var ft = FragmentManager.BeginTransaction();
ft.AddToBackStack(null);
switch (e.MenuItem.ItemId)
{
case (Resource.Id.nav_incidents):
SupportActionBar.SetTitle(Resource.String.toolbar_Test);
ft.Add(Resource.Id.HomeFrameLayout, new Test());
break;
}
ft.Commit();
ft.Dispose();
// Close drawer
drawerLayout.CloseDrawers();
}
Fragment
[Activity(Label = "Test")]
public class Test : Fragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.Inflate(Resource.Layout.Test, container, false);
return view;
}
}
Xamarin Profiler
you have to check fragment is available before you add new one
switch (e.MenuItem.ItemId)
{
case (Resource.Id.nav_incidents):
SupportActionBar.SetTitle(Resource.String.toolbar_Test);
Fragment myFragment =
(Fragment)FragmentManager.FindFragmentByTag("FRAGMENT1");
if (myFragment.IsVisible){
ft.Replace(Resource.Id.HomeFrameLayout, new Test(),"FRAGMENT1");
}else{
ft.Add(Resource.Id.HomeFrameLayout, new Test(),"FRAGMENT1");
}
break;
}
Hope this help

ActionBar With Tabbar in Xamarin forms for all(android, iOs and WinPhone)

i need to Create a ActionBar with TabbedLayout control in xamarin forms, In xamarin Android i did that Easily but now they want in both platform IOS and Android using Xamarin forms.please share any Example or Give me suggestion for how to make the custom Controls in Xamari Froms.
Below i have attached the Image how i need Action bar with Tabbed layout.
If you are using Xamarin.Forms the Tabbed page, for Android tabbar items will in the top. For iOS, you have to create a renderer to achieve it. However, Showing Tabbar items in the top are against User guidelines of iOS.
Create custom render, override ViewDidLayoutSubviews and add the following lines code.
[assembly: ExportRenderer(typeof(ExtendedTabbedPage), typeof(ExtendedTabbedPageRenderer))]
namespace ExtendedTabbedPage.Pages
{
public class ExtendedTabbedPageRenderer : TabbedRenderer
{
private ExtendedTabbedPage Page => (ExtendedTabbedPage)Element;
public ExtendedTabbedPageRenderer()
{
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
var page = (ExtendedTabbedPage)Element;
page.CurrentPageChanged += Page_CurrentPageChanged;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
Page_CurrentPageChanged();
}
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
SetTabPostition();
}
void SetTabPostition()
{
if (Element == null)
return;
var element = Element as ExtendedTabbedPage;
this.TabBar.InvalidateIntrinsicContentSize();
nfloat tabSize = 74.0f;
UIInterfaceOrientation orientation = UIApplication.SharedApplication.StatusBarOrientation;
if (UIInterfaceOrientation.LandscapeLeft == orientation || UIInterfaceOrientation.LandscapeRight == orientation)
{
tabSize = 32.0f;
}
CGRect tabFrame = this.TabBar.Frame;
CGRect viewFrame = this.View.Frame;
tabFrame.Height = tabSize;
tabFrame.Y = this.View.Frame.Y;
this.TabBar.Frame = tabFrame;
this.TabBar.ContentMode = UIViewContentMode.Top;
PageController.ContainerArea = new Rectangle(0, tabFrame.Y + tabFrame.Height, viewFrame.Width, viewFrame.Height - tabFrame.Height);
this.TabBar.SetNeedsUpdateConstraints();
}
void Page_CurrentPageChanged()
{
var current = Tabbed.CurrentPage;
//if Tab is more than 5 then more will appear in iOS
if (current == null)
{
CGRect tabFrm = this.TabBar.Frame;
if (this.MoreNavigationController != null)
{
var morenavframe = this.MoreNavigationController.View.Frame;
morenavframe.Y = tabFrm.Y + tabFrm.Height;
this.MoreNavigationController.View.Frame = morenavframe;
foreach (var morecontroller in this.MoreNavigationController.ViewControllers)
{
var morecontframe = morecontroller.View.Frame;
morecontframe.Y = morenavframe.Y + morenavframe.Height;
morecontroller.View.Frame = tabFrm;
}
}
return;
}
var controller = Platform.GetRenderer(current);
if (controller == null)
return;
var frame = controller.ViewController.View.Frame;
CGRect tabFrame = this.TabBar.Frame;
frame.Y = (tabFrame.Y + tabFrame.Height);
controller.ViewController.View.Frame = frame;
this.View.Frame = frame;
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
Page_CurrentPageChanged();
}
}
}
To get a tabbed layout in Xamarin.Forms you'll usually use a TabbedPage. This will give you the tabs you show on Android. On iOS and Windows you'll get the native alternative. This means you'll get the tabs on the bottom of the screen on iOS and on Windows you'll get the tabs on top (similar, but exactly like on Android). See this illustration from the Xamarin docs:
If you want to create your own version you can implement your own version of the MultiPage class.

Adding a bottom border to an Entry in Xamarin Forms iOS with an image at the end

Now before anyone ignores this as a duplicate please read till the end. What I want to achieve is this
I've been doing some googling and looking at objective c and swift responses on stackoverflow as well. And this response StackOverFlowPost seemed to point me in the right direction. The author even told me to use ClipsToBounds to clip the subview and ensure it's within the parents bounds. Now here's my problem, if I want to show an image on the right side of the entry(Gender field), I can't because I'm clipping the subview.
For clipping, I'm setting the property IsClippedToBounds="True" in the parent stacklayout for all textboxes.
This is the code I'm using to add the bottom border
Control.BorderStyle = UITextBorderStyle.None;
var myBox = new UIView(new CGRect(0, 40, 1000, 1))
{
BackgroundColor = view.BorderColor.ToUIColor(),
};
Control.AddSubview(myBox);
This is the code I'm using to add an image at the beginning or end of an entry
private void SetImage(ExtendedEntry view)
{
if (!string.IsNullOrEmpty(view.ImageWithin))
{
UIImageView icon = new UIImageView
{
Image = UIImage.FromFile(view.ImageWithin),
Frame = new CGRect(0, -12, view.ImageWidth, view.ImageHeight),
ClipsToBounds = true
};
switch (view.ImagePos)
{
case ImagePosition.Left:
Control.LeftView.AddSubview(icon);
Control.LeftViewMode = UITextFieldViewMode.Always;
break;
case ImagePosition.Right:
Control.RightView.AddSubview(icon);
Control.RightViewMode = UITextFieldViewMode.Always;
break;
}
}
}
After analysing and debugging, I figured out that when OnElementChanged function of the Custom Renderer is called, the control is still not drawn so it doesn't have a size. So I subclassed UITextField like this
public class ExtendedUITextField : UITextField
{
public UIColor BorderColor;
public bool HasBottomBorder;
public override void Draw(CGRect rect)
{
base.Draw(rect);
if (HasBottomBorder)
{
BorderStyle = UITextBorderStyle.None;
var myBox = new UIView(new CGRect(0, 40, Frame.Size.Width, 1))
{
BackgroundColor = BorderColor
};
AddSubview(myBox);
}
}
public void InitInhertedProperties(UITextField baseClassInstance)
{
TextColor = baseClassInstance.TextColor;
}
}
And passed the hasbottomborder and bordercolor parameters like this
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
var view = e.NewElement as ExtendedEntry;
if (view != null && Control != null)
{
if (view.HasBottomBorder)
{
var native = new ExtendedUITextField
{
BorderColor = view.BorderColor.ToUIColor(),
HasBottomBorder = view.HasBottomBorder
};
native.InitInhertedProperties(Control);
SetNativeControl(native);
}
}
But after doing this, now no events fire :(
Can someone please point me in the right direction. I've already built this for Android, but iOS seems to be giving me a problem.
I figured out that when OnElementChanged function of the Custom Renderer is called, the control is still not drawn so it doesn't have a size.
In older versions of Xamarin.Forms and iOS 9, obtaining the control's size within OnElementChanged worked....
You do not need the ExtendedUITextField, to obtain the size of the control, override the Frame in your original renderer:
public override CGRect Frame
{
get
{
return base.Frame;
}
set
{
if (value.Width > 0 && value.Height > 0)
{
// Use the frame size now to update any of your subview/layer sizes, etc...
}
base.Frame = value;
}
}

Valid way to hide QLPreviewController RightBarButtonItem/RightBarButtonItems Xamarin iOS

My app supports iOS8+ devices. I want to hide right Action button from navigation bar. By research I found following few workarounds:
1. Create Sub class of QLPreviewController and in ViewDidAppear SetRightBarButtonItems to zero.
public class PdfViewController : QLPreviewController
{
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
NavigationItem.SetRightBarButtonItems (new UIKit.UIBarButtonItem[0], false);
}
}
In this case problem is RightBarButtonItem appears and then disappears. In mean while user is able to click on that RightBarButtonItem button. I don't want this behavior.
2. Create UIViewController and add QLPreviewController as child ViewController.
void BtnShowPdf_Clicked (object sender, EventArgs e) {
var dummyVC = new UIViewController ();
var pdfVC = new PdfViewController ();
dummyVC.AddChildViewController (pdfVC);
dummyVC.View.AddSubview (pdfVC.View);
dummyVC.NavigationItem.SetRightBarButtonItems (new UIBarButtonItem[0], false);
dummyVC.EdgesForExtendedLayout = UIRectEdge.None;
dummyVC.AutomaticallyAdjustsScrollViewInsets = false;
dummyVC.View.BackgroundColor = UIColor.Clear;
pdfVC.EdgesForExtendedLayout = UIRectEdge.None;
pdfVC.AutomaticallyAdjustsScrollViewInsets = false;
pdfVC.View.BackgroundColor = UIColor.Clear;
}
In this case If I set QLPreviewController it works as expected. But NavigationBar becomes more darker than default ViewController background color.
Dark Bar:
http://screencast.com/t/bqVMv5qqGz
Needed clear background bar like:
http://screencast.com/t/MUwE2VnxJ7
My questions are:
A) I would like to know which is the correct way to hide right
navigation bar button as per Apple guidelines ? If you have correct
solution then those are also appreciated.
B) Also Can you please suggest solution(s) for #1 Or #2 ?
Pretty sure you can do this:
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
self.navigationItem.rightBarButtonItems = nil
}

Resources