https://www.c-sharpcorner.com/article/how-to-create-and-use-a-custom-switch-control-in-xamarin-forms/
"OnImage" and "OffImage" does not work when i make a custom renderer following the guide above. I see no image when i click on or off.
In my class, I send in a HTTP url and then convert that URL to a UIImage. It works successfully as i've added breakpoints all the way. I even tried with a image in my library to see if that would change the outcome but it didnt.
public class Switch_iOS : SwitchRenderer
{
Version version = new Version(ObjCRuntime.Constants.Version);
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || e.NewElement == null)
return;
var view = (CustomSwitch)Element;
if (!string.IsNullOrEmpty(view.SwitchThumbImage))
{
if (version > new Version(6, 0))
{
Control.OnImage = FromUrl(view.SwitchThumbImage);
Control.OffImage = FromUrl(view.SwitchThumbImage);
}
else
{
Control.ThumbTintColor = view.SwitchThumbColor.ToUIColor();
}
}
}
public UIImage FromUrl(string uri)
{
using (var url = new NSUrl(uri))
using (var data = NSData.FromUrl(url))
return UIImage.LoadFromData(data);
}
}
Is "Control.OnImage" and "Control.OffImage" not working with the latest Xamarin Forms? I also intend to make a background image to the switch.
Unfortunately , about OnImage and OffImage of UISwitch , from iOS 7 has no effect.
Have a look at the apple document :
In iOS 7 and later, this property has no effect.
In iOS 6, this image represents the interior contents of the switch.
The image you specify is composited with the switch’s rounded bezel
and thumb to create the final appearance.
Instead you can use
onTintColor to set the tint color of the switch when it is turned on.
tintColor to set the tint color of the switch when it is turned off.
thumbTintColor to set the tint color of the thumb.
Related
Xamarin Forms IconImageSource:
On Android Toolbar image is shown correctly, on iOS a white image is shown. How can I fix this?
I added file to Resources folder and set build Action to BundleResource.
If I change filename to non excisting file, image is not shown at all. I tried changing extension to JPG, same result, also a white image is shown.
I used this code snippit.
ToolbarItem toolbarItemSearch = new ToolbarItem { IconImageSource = ImageSource.FromFile("searchIcon.png") };
ToolbarItems.Add(toolbarItemSearch);
iOS has tinting the ToolbarItems by default , the color is blue/white .
To solve it we need to create a custom renderer for NavigationPage , and set the TintColor to transparent , and set the image with another rendering mode .
Sample code
[assembly: ExportRenderer(typeof(NavigationPage), typeof(MyRenderer))]
namespace FormsApp.iOS
{
class MyRenderer : NavigationRenderer
{
public override void PushViewController(UIViewController viewController, bool animated)
{
base.PushViewController(viewController, animated);
var currentPage = (this.Element as Xamarin.Forms.NavigationPage)?.CurrentPage;
if (this.NavigationBar == null || currentPage == null)
return;
var buttonItems = TopViewController.NavigationItem.RightBarButtonItems;
foreach (var button in buttonItems)
{
if(button.Image != null){
button.Image = button.Image.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
button.TintColor = UIColor.Clear;
}
}
}
}
}
Refer to
Image in Toolbar item on iOS are white(Xamarin Forms)
The solution from #ColeX - MSFT did the trick! Additionally make sure to do a null pointer check for non Image buttons.
I am trying to set icons on tabs in my Xamarin app's TabbedView based on the state of the page displayed on the tab. I am able to access a tab individually and setting its icon during rendering on an Android phone emulator, but it does not work on Android tablet emulator.
I am using the DispatchDraw method in a TabbedRenderer based on this post, but I don't have a good understanding though of how that method is called and am having difficulty finding guidance on it.
My code does get called in both devices, but I find that (this.Context as Activity).ActionBar.TabCount is 0 in the tablet every time I check it, while it it is 0 initially but then DispatchDraw is called a second time and its value at that point is 2 (i.e. the tabs have loaded) on the phone, but not on the tablet. That's what allows me to set the icon on the phone, but not the tablet.
Any idea what would differ based only on the change of emulators, to make them render differently in this way?
Here is my TabbedRenderer code:
[assembly: ExportRenderer(typeof(FormTabbedPage), typeof(ExtendedTabbedPageRenderer))]
namespace MyNamespace
{
public class ExtendedTabbedPageRenderer: TabbedRenderer
{
public ExtendedTabbedPageRenderer(Context context) : base(context) { }
protected override void DispatchDraw(global::Android.Graphics.Canvas canvas)
{
base.DispatchDraw(canvas);
SetTabIcons();
}
private void SetTabIcons()
{
var element = this.Element;
if (null == element)
{
return;
}
Activity activity = this.Context as Activity;
if ((null != activity && null != activity.ActionBar && activity.ActionBar.TabCount > 0))
{
bool hasErrors = false;
for (int i = 0; i < element.Children.Count; i += 1)
{
hasErrors = ((i % 2) > 0); // This is a placeholder for app specific logic based on page content
int resourceID = Resources.GetIdentifier("error", "drawable", Context.PackageName);
if (hasErrors)
{
Drawable icon = Context.GetDrawable(resourceID);
Android.App.ActionBar.Tab tab = activity.ActionBar.GetTabAt(i);
tab.SetIcon(icon);
}
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// do nothing - I have this method for debugging purposes
}
}
I find that on the tablet, if I initially render the form, I don't get icons, but if I then click on one of the tabs, and if I run the tab logic in OnElementPropertyChanged method instead of DispatchDraw at that point, by then the tabs on the ActionBar ARE populated and I DO get the icons showing.
(Sidenote: one other odd thing is that there is a warning over the ActionBar class saying it's obsolete - not sure what to make of that, since the Activity class contains it as a property and that isn't obsolete.)
Many thanks!
I have a custom WkWebView where I set the height of the view to the size of the html content.
This works great when I initialize it, but the problem comes when I change the source of the WkWebView to a shorter html.
I've already encountered this problem in the android version of my app and I fixed that by setting the HeightRequest to 0 before EvaluateJavaScriptAsync. In that way the view will always get bigger.
I tried the same thing with iOS but it keeps the highest content I had .
So for example :
If I set the source of the WkWebView to a html file that is 200px height and change it to a html file that is 1000px, it works fine and I can see all the content. BUT, if I try to go back to my 200px html file, I get a 800px blank space underneath and keep the 1000px height.
The goal is to always have the height of the WKWebView to adapt to the height of its content.
Here is the current version of the custom render
namespace MyNamespace.iOS.CustomControl
{
public class AutoHeightWebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
try
{
base.OnElementChanged(e);
if (NativeView != null)
{
var webView = (WKWebView)NativeView;
NavigationDelegate = new ExtendedWKWebViewDelegate(this);
}
}
catch (Exception ex)
{
//Log the Exception
}
}
}
class ExtendedWKWebViewDelegate : WKNavigationDelegate
{
AutoHeightWebViewRenderer webViewRenderer;
public ExtendedWKWebViewDelegate(AutoHeightWebViewRenderer _webViewRenderer = null)
{
webViewRenderer = _webViewRenderer ?? new AutoHeightWebViewRenderer();
}
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
try
{
var _webView = webViewRenderer.Element as AutoHeightWebView;
if (_webView != null)
{
_webView.HeightRequest = 0d;
await Task.Delay(300);
var result = await _webView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()");
_webView.HeightRequest = Convert.ToDouble(result);
}
}
catch (Exception ex)
{
//Log the Exception
}
}
}
}
EDIT 1 :
To be clearer, I'm able to change the height of the webview, but I dont know the height because it always returned the height of the largest html displayed so far. Nomatter if I use _webView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()") or webView.ScrollView.ContentSize.Height.
EDIT 2 :
Here a little sample to help understand the problem. I got two buttons and my custom webview (initialize with a 40 HeightRequest empty html).
The first button set the Source of the webview to a 70px long HTML. The second one set the Source to a 280px long HTML.
In this example, I click on the first button than the second one and finaly back on the first button again. You see the webview getting bigger on the first 2 clicks. But then then webview should get shrunk when I choose back the first html (passing from 280px to 70px) but it keeps the 280px long.
First button (70px long)
Second button (280px long)
Back to the first button (should be 70px long instead of 280px).
The problem occured on both simulator and iOS device.
You can change the Frame of webView to change the height.
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
try
{
var _webView = webViewRenderer.Element as WebView;
if (_webView != null)
{
webView.Frame = new CGRect(webView.Frame.Location, new CGSize(webView.Frame.Size.Width, 0));
var a = webView.ScrollView.ContentSize.Height;
var b = await _webView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()");
Console.WriteLine(a);
Console.WriteLine(b);
CGRect tempRect = webView.Frame;
// for test
webView.Frame = new CGRect(tempRect.Location.X, tempRect.Location.Y, tempRect.Size.Width, float.Parse(b));
}
}
catch (Exception ex)
{
//Log the Exception
}
}
I had the same problem and the CustomNavigationDelegate would also only show the same large size, which was the Device Display size.
I found that I had set this on the init part of the XAML and code behind, which somehow overrides the later content-based calculation.
See my fix here: https://stackoverflow.com/a/62409859/3443564
I am trying to add background image to an entry in xamarin forms and i wrote code for rendering in ios and android. For android it is working and for ios it is not working.
Here I am sharing rendering code for ios:
[assembly: ExportRenderer (typeof(ExtendedUsername), typeof(RenderingEntryuname))]
namespace ifind.iOS
{
public class RenderingEntryuname: EntryRenderer
{
// Override the OnElementChanged method so we can tweak this renderer post-initial setup
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null) {
Control.TextAlignment=UITextAlignment.Center;
UIImage img=UIImage.FromFile("Images/newimg.jpg");
Control.Background=img
}
}
}
}
By default the UITextEntry used for a Forms Entry will have its border style set to UITextBorderStyle.RoundedRect and thus any background will be ignored.
Set the border style to none and your image will show up:
if (Control != null) {
Control.BorderStyle = UITextBorderStyle.None;
Control.TextAlignment=UITextAlignment.Center;
UIImage img=UIImage.FromFile("Images/newimg.jpg");
Control.Background=img
}
If the value is set to the UITextBorderStyleRoundedRect style, the custom background image associated with the text field is ignored.
Ref: UITextField
For example, I have a label on my page:
var label = new Label
{
Text = "Some text here.",
LineBreakMode = LineBreakMode.WordWrap,
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
};
How do I make this label's font size increase (or decrease) depending on the user's accessibility settings for font sizes? For example, in iOS you can set the Font Size for your device under Settings > General > Accessibility > Larger Text. I believe that Apple calls this "Dynamic Text" and is almost a requirement for your app to support.
The same applies for other controls in my app (buttons, entrys, etc).
I have tried this setting on my iPhone and it does not appear to be changing all things in my app. There are a few things like TableView section headers and list view cells that are changing, but things like my standard Labels and Entrys are not.
You would need to supply the UIFont returned from preferredFontWithTextStyle (C# = UIFont.PreferredFontForTextStyle) as your usage context of a label, button, entry, etc... would not be known to Xamarin.Forms.
So what I did for one client was subclass the base renderers and view elements and add an iOS-only property to those elements so they could define the context of how that control is begin used in the UI and thus when rendered by iOS these controls will be subject to Dynamic Text sizing.
There are six Dynamic font types defined in iOS 9:
UICTFontTextStyleBody
UICTFontTextStyleCaption1
UICTFontTextStyleCaption2
UICTFontTextStyleFootnote
UICTFontTextStyleHeadline
UICTFontTextStyleSubhead
Note: Xamarin.iOS does not have constants/enum defined for these like Swift does (ObjC does not define these either), so they are passed as a NSString, see example below.
Example Renderer:
Sets UICTFontTextStyleBody for a label subclass called BodyLabel:
[assembly: ExportRenderer(typeof(BodyLabel), typeof(iOSLabelBodyRenderer))]
namespace Foobar.iOS
{
public class iOSLabelBodyRenderer : LabelRenderer
{
public iOSLabelBodyRenderer() { }
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null)
Control.Font = UIFont.GetPreferredFontForTextStyle(new NSString("UICTFontTextStyleBody"));
}
}
}
Results in:
Note: Technically you should also implement UIContentSizeCategoryDidChangeNotification notifications so you resize/invalidate your controls when the user changes the dynamic font size.
Using the info in the answer provided #SushiHangover, I was able to implement the following renderer strategy for iOS:
Create a renderer for each control that you want to support dynamic text on. In my case, this included Labels, Buttons, and Entrys. The renderers assign the Control.Font to a UIFontDescriptor with the PointSize percentage adjusted based on the NamedSize assigned to the Xamarin Forms control.
Example:
[assembly: ExportRenderer(typeof(Button), typeof(CustomButtonRenderer))]
namespace iOS.Controls
{
public class CustomButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
var view = e.NewElement as Button;
if(Control != null && view != null)
{
var descriptor = UIFontDescriptor.PreferredBody;
var pointSize = descriptor.PointSize;
var size = view.FontSize;
if(size == Device.GetNamedSize(NamedSize.Large, typeof(Button)))
{
pointSize *= 1.4f;
}
else if(size == Device.GetNamedSize(NamedSize.Small, typeof(Button)))
{
pointSize *= .8f;
}
else if(size == Device.GetNamedSize(NamedSize.Micro, typeof(Button)))
{
pointSize *= .6f;
}
Control.Font = UIFont.FromDescriptor(descriptor, pointSize);
}
}
}
}
You could even dynamically support fixed font sizes this way by taking their percentage value based on the base NamedSize.Default size.
Example:
[assembly: ExportRenderer(typeof(Button), typeof(CustomButtonRenderer))]
namespace iOS.Controls
{
public class CustomButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
var view = e.NewElement as Button;
if(Control != null && view != null)
{
var descriptor = UIFontDescriptor.PreferredBody;
var percent = view.FontSize / Device.GetNamedSize(NamedSize.Default, typeof(Button));
Control.Font = UIFont.FromDescriptor(descriptor, percent * descriptor.PointSize);
}
}
}
}
For reference, full implementation examples can be found in this GitHub project.
For iOS I believe things have changed in Xamarin.Forms since the earlier answers were provided. If a NamedSize value is assigned to the FontSize property of a text element, the text now gets scaled up and down with the value set via the iOS "Larger Text" setting.
For example, this would result in accessible, scalable text:
<Label
FontSize="Body"
LineBreakMode="WordWrap"
Text="Some text here." />