I wanted to hide 'cancel' button in my iOS search bar. I have implemented the following custom renderer code but it seems not to to work. If anyone knows solution , please share.
public class iOSSearchBar : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> args)
{
base.OnElementChanged(args);
UISearchBar bar = (UISearchBar)this.Control;
bar.AutocapitalizationType = UITextAutocapitalizationType.None;
bar.AutocorrectionType = UITextAutocorrectionType.No;
//bar.BarStyle = UIBarStyle.Default;
//bar.BarTintColor = UIColor.LightGray;
//bar.KeyboardType = UIKeyboardType.ASCIICapable;
bar.SearchBarStyle = UISearchBarStyle.Minimal;
bar.SetShowsCancelButton(false, false);
bar.ShowsCancelButton = false;
}
}
Thanks in advance
This worked for me.
https://gist.github.com/xleon/9f94a8482162460ceaf9
using System;
using Xamarin.Forms.Platform.iOS;
using Xamarin.Forms;
using UIKit;
using System.Diagnostics;
[assembly: ExportRenderer(typeof(SearchBar), typeof(Namespace.iOS.Renderers.ExtendedSearchBarRenderer))]
namespace Namespace.iOS.Renderers
{
public class ExtendedSearchBarRenderer : SearchBarRenderer
{
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "Text")
{
Control.ShowsCancelButton = false;
}
}
}
}
Write code to hide cancel button in layoutsubviews method.
public override void LayoutSubviews()
{
base.LayoutSubviews();
UISearchBar bar = (UISearchBar)this.Control;
bar.ShowsCancelButton = false;
}
Following is also working or me, no need to subclass searcher:
SearchBar.TextChanged += delegate
{
SearchBar.ShowsCancelButton = false;
};
I think i managed to remove it manually with:
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Subviews[0].Subviews[0].RemoveFromSuperview();
}
}
I spend some more time searching for this, so adding here in case someone else wants to do both.
In case you also need to remove the X button as well, I found the solution in this comment
Related
I have tried the custom renderer stated here on my project
https://blog.falafel.com/adding-transparency-listview-ios-xamarin-forms-custom-renderer/
and
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/ios/theme
where I placed the following code below in the finished launching function of app delegate.cs file
// switch
UISwitch.Appearance.OnTintColor = UIColor.FromRGB(0x91, 0xCA, 0x47); // green
UITableViewCell.Appearance.TintColor=UIColor.Yellow
But both to no avail. Nothing seems to have changed on the UI and I am not sure if I am missing out on something. Anyone able to help me out with this?
I have tried every possible way on the web (I think). And the only way that worked is to make a BoxView with a touch-inside event that fires when user touches the BoxView. Then add that BoxView to your cell's background (presuming that you're using a custom cell for your TableView or ListView). After that you'll have to change the Color property of the BoxView whenever a user touches it.
And since Xamarin forms does not support any touch gesture except for taps. We will need to create our own.
How To Create a Custom BoxView
The iOS Renderer (In Xamarin.iOS):
using System;
using something;
using something.iOS;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(MyBoxView), typeof(MyBoxViewRenderer))]
namespace something.iOS
{
public class MyBoxViewRenderer : BoxRenderer
{
public MyBoxViewRenderer()
{
}
public override void TouchesBegan(Foundation.NSSet touches, UIEvent evt)
{
if (Element == null)
return;
var touch = touches.AnyObject as UITouch;
(Element as MyBoxView).SendTouchEvent(Element as MyBoxView, true);
}
public override void TouchesEnded(Foundation.NSSet touches, UIEvent evt)
{
if (Element == null)
return;
var touch = touches.AnyObject as UITouch;
(Element as MyBoxView).SendTouchEvent(Element as MyBoxView, false);
}
public override void TouchesCancelled(Foundation.NSSet touches, UIEvent evt)
{
if (Element == null)
return;
var touch = touches.AnyObject as UITouch;
(Element as MyBoxView).SendTouchEvent(Element as MyBoxView, false);
}
}
}
MyBoxView:
using System;
using Xamarin.Forms;
namespace something
{
public class MyBoxView : BoxView
{
public event TouchChanged OnTouchChanged = delegate { };
public delegate void TouchChanged(object sender, bool IsTouched);
public void SendTouchEvent(object sender, bool IsTouched)
{
OnTouchChanged(sender, IsTouched);
}
public MyBoxView()
{
}
}
}
Hope that helps!
You can create a custom renderer for ViewCell, then try to change the selected color like:
[assembly: ExportRenderer(typeof(ViewCell), typeof(MyViewCellRenderer))]
namespace ProjectName.iOS
{
public class MyViewCellRenderer : ViewCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var cell = base.GetCell(item, reusableCell, tv);
cell.SelectedBackgroundView = new UIView(cell.Bounds);
cell.SelectedBackgroundView.BackgroundColor = UIColor.Red;
return cell;
}
}
}
How to change the color of the switch button when toggling for IOS Xamarin Forms? Custom render will set the color just once:
Xamarin forms toggle button default color on IOS
and it won't be changed when the Switch is toggled. Same goes and for setting values in AppDelegate.cs it will be done only once. I need this:
In IOS its only possible using:
UISwitch.Appearance.ThumbTintColor = UIColor.Brown;
UISwitch.Appearance.OnTintColor = UIColor.Red;
but how I can access that field if my switch is in PCL project, and its used for Xamarin Forms.
I solved this with:
public class CustomSwitchRenderer: SwitchRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
{
Element.Toggled += ElementToggled;
base.OnElementChanged(e);
if (Control != null)
{
UpdateUiSwitchColor();
}
}
private void ElementToggled(object sender, ToggledEventArgs e)
{
UpdateUiSwitchColor();
}
private void UpdateUiSwitchColor()
{
var temp = Element as Switch;
if (temp.IsToggled)
{
Control.ThumbTintColor = Color.FromHex(ColorConstants.BlueHex).ToUIColor();
Control.OnTintColor = Color.FromHex(ColorConstants.OblueLightHex).ToUIColor();
}
else
{
Control.ThumbTintColor = Color.FromHex(ColorConstants.GrayHex).ToUIColor();
}
}
}
So, I activated event whenever Toggle is executed:
Element.Toggled += ElementToggled;
You can still do it in a custom renderer. Just listen to the switch's ValueChanged event, there you can check if it is on or off, and update the color.
For example, in your iOS project's custom switch renderer class:
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
{
base.OnElementChanged(e);
Control.ValueChanged += Control_ValueChanged;
}
private void Control_ValueChanged(object sender, EventArgs e)
{
if (Control.On)
{
// set on colors
}
else
{
// set off colors
}
}
I would like to ask if there is a way to make a Xamarin.Forms.Label tell that it should use the "AdjustsFontSizeToFitWidth" when the app is run on iOS.
Is this possible, or is Xamarin.Forms.Label something completely different than the UILabel?
I was thinking that Xamarin.Form.Label "falls back" on the UILabel when it's built for iOS, is that the case?
Edit:
According to how I understood the comments I tried this, but it wouldn't work:
switch (Device.RuntimePlatform)
{
case Device.iOS:
{
_label1.AdjustsFontSizeToFitWidth = true;
break;
}
default:
{
break;
}
}
The error I'm getting is that "AdjustsFontSizeToFitWidth" isn't a property of Xamarin.Forms.Label.
Edit 2:
According to another comment I tried the following:
public class clsGridCell : ContentView
{
private Xamarin.Forms.Label _label1;
//problem is that it's not longer a Xamarin.Forms.Label, but UILabel on iOS, but I can't declare it as "var" when I want to reuse it for binding.
//declaring it as "object" would break the bindings.
public clsGridCell()
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
{
_label1 = new UILabel
_label1.AdjustsFontSizeToFitWidth = true;
break;
}
default:
{
_label1 = new Xamarin.Forms.Label()
{
HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Center,
};
break;
}
}
this._label1.SetBinding(Label.BackgroundColorProperty, new Binding() { Path = "BackgroundColor", Source = this });
this._label1.SetBinding(Label.TextProperty, new Binding() { Path = "Text", Source = this });
this._label1.SetBinding(Label.TextColorProperty, new Binding() { Path = "TextColor", Source = this });
The error is in line "_label1.AdjustsFontSizeToFitWidth = true;".
The error thrown is "Label doesn't contain "AdjustsFontSizeToFitWidth"".
That is because I declared it as "Label".
I did that because "var" wasn't possible in this case.
What would be an alternative way to declare it without breaking the bindings?
Declaring it as "object" would break the bindings.
Thank you!
As SushiHangover mentioned above,there are several ways to achieve your requirement.
Use Effects
In PCL
label.Effects.Add (Effect.Resolve ("MyCompany.AdjustsFontSizeEffect"));
Create class named AdjustsFontSizeEffect In iOS Project
[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(AdjustsFontSizeEffect), "AdjustsFontSizeEffect")]
namespace EffectsDemo.iOS
{
public class AdjustsFontSizeEffect : PlatformEffect
{
protected override void OnAttached ()
{
(Control as UILabel).AdjustsFontSizeToFitWidth = true;
}
protected override void OnDetached ()
{
}
}
}
Use Custom Renderers
Create class named AdjustsFontSizeEffectRenderer In iOS Project
[assembly: ExportRenderer(typeof(Label), typeof(AdjustsFontSizeEffectRenderer))]
namespace EffectsDemo.iOS
{
public class AdjustsFontSizeEffectRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.AdjustsFontSizeToFitWidth = true;
}
}
}
}
I recommend you to use Effectsin this scenario.
Refer to Why Use an Effect over a Custom Renderer?
I have created a custom renderer for a WebView in my Xamarin.Forms project. The WebView works great on Android. Unfortunately, on iOS, when the device is rotated, the webpage (any webpage) does not correctly adjust to the new dimensions of the WebView. If I use the built in WebView for Forms, the pages correctly resize on rotate.
What am I doing wrong with my custom renderer?
Below is a stripped down version of my custom renderer (the issue still occurs):
using Xamarin.Forms.Platform.iOS;
using Xamarin.Forms;
using UIKit;
using Foundation;
[assembly: ExportRenderer(typeof(WebView), typeof(MyProject.iOS.WebViewRenderer))]
namespace MyProject.iOS {
public class WebViewRenderer : ViewRenderer<WebView, UIWebView> {
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e) {
if (Control == null) {
var webView = new UIWebView(Frame);
webView.AutoresizingMask = UIViewAutoresizing.All;
SetNativeControl(webView);
webView.LoadRequest(new NSUrlRequest(new NSUrl("http://cnn.com")));
}
}
}
}
To fix the issue, add the following to the custom renderer:
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) {
return new SizeRequest(Size.Zero, Size.Zero);
}
To fix the rotate problem you need to use the NativeView.Bounds when you create the UIWebView.
[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespaceMobile.App.iOS
{
public class CustomWebViewRenderer : ViewRenderer<CustomWebView, UIWebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var webView = new UIWebView(NativeView.Bounds);
webView.ShouldStartLoad += HandleShouldStartLoad;
webView.AutoresizingMask = UIViewAutoresizing.All;
webView.ScalesPageToFit = true;
SetNativeControl(webView);
webView.LoadRequest(new NSUrlRequest(new NSUrl(Element.Url)));
}
}
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
return new SizeRequest(Size.Zero, Size.Zero);
}
}
}
I am developing an app for iOS using MvvmCross. On one of my Views I have some basic report data that is displayed in a tableview.
When the table row is touched a new view containing a detail report is displayed by making the call to ShowViewModel passing some parameters in a Dictionary. This works fine.
When the user swipes left or right the app needs to show the detail report for the next or previous item in the original list. I am doing this by updating some parameters and calling ShowViewModel again. The logic behind this is all working fine.
My problem; ShowViewModel animates the new view coming in from the right. This is perfect when the user has swiped left. However when swiping right it seems counter intuitive. How can I make ShowViewModel animate or transition in from the left side?
if you look to the MvvmCross source code here you see how the default behavior is showing the ViewControllers
You need to change that by doing something like the following:
How to change the Push and Pop animations in a navigation based app
for that, one idea is to have a custom view presenter and catch navigation to that particular view-model (override Show(IMvxTouchView view) )
or, maybe derive from UINavigationController, set it to MvvmCross to use it (look to the MvxSetup), and on some events change transition to that particular view
similar to this question
How to specify view transitions on iPhone
This is the solution I was able to come up with following the helpful pointers in the answer from Andrei N. In the end I opted for a TransitionFlipFromRight and TransitionFlipFromLeft when scrolling between detail reports. Hopefully it is useful to somebody else.
I already had a presenter class that was inherited from MvxModalSupportTouchViewPresenter
public class BedfordViewPresenter : MvxModalSupportTouchViewPresenter
Within this class I added a property of MvxPresentationHint.
private MvxPresentationHint _presentationHint;
In the override of method ChangePresentation the above property is used to store the passed in parameter
public override void ChangePresentation (MvxPresentationHint hint)
{
_presentationHint = hint;
base.ChangePresentation (hint);
}
Two new MvxPresentationHint class were declared (see later)
In the presenter class the Show method was overridden
public override void Show(IMvxTouchView view)
{
if (_presentationHint is FlipFromRightPresentationHint) {
var viewController = view as UIViewController;
MasterNavigationController.PushControllerWithTransition (viewController, UIViewAnimationOptions.TransitionFlipFromRight);
}
else
if (_presentationHint is FlipFromLeftPresentationHint) {
var viewController = view as UIViewController;
MasterNavigationController.PushControllerWithTransition (viewController, UIViewAnimationOptions.TransitionFlipFromLeft);
}
else {
base.Show (view);
}
_presentationHint = null;
}
A new class that provides extensions to a UINavigationController was created with the method PushControllerWithTransition
public static class UINavigationControllerExtensions
{
public static void PushControllerWithTransition(this UINavigationController
target, UIViewController controllerToPush,
UIViewAnimationOptions transition)
{
UIView.Transition(target.View, 0.75d, transition, delegate() {
target.PushViewController(controllerToPush, false);
}, null);
}
}
All that needs to be defined now are the two new MvxPresentationHint class derivations. These belong in your Core class library project rather than the iOS application project.
public class FlipFromLeftPresentationHint : MvxPresentationHint
{
public FlipFromLeftPresentationHint ()
{
}
}
and
public class FlipFromRightPresentationHint: MvxPresentationHint
{
public FlipFromRightPresentationHint ()
{
}
}
I hope this is a help to someone else trying to do something similar
Share my solution for android:
On view:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
var layout = view.FindViewById<LinearLayout>(Resource.Id.swippeable);
var swipeListener = new SwipeListener(this.Activity);
swipeListener.OnSwipeLeft += (sender, e) => this.ViewModel.LeftCommand?.Execute(); //Here use command into view model
swipeListener.OnSwipeRight += (sender, e) => this.ViewModel.RightCommand?.Execute();
layout.SetOnTouchListener(swipeListener);
return view;
}
Gesture listener:
public class SwipeListener : SimpleOnGestureListener, View.IOnTouchListener
{
private const int SWIPE_THRESHOLD = 100;
private const int SWIPE_VELOCITY_THRESHOLD = 100;
private readonly GestureDetector gestureDetector;
public SwipeListener(Context ctx)
{
this.gestureDetector = new GestureDetector(ctx, this);
}
public Boolean OnTouch(View v, MotionEvent e)
{
return this.gestureDetector.OnTouchEvent(e);
}
public event EventHandler OnSwipeRight;
public event EventHandler OnSwipeLeft;
public event EventHandler OnSwipeTop;
public event EventHandler OnSwipeBottom;
public override Boolean OnDown(MotionEvent e)
{
return true;
}
public override Boolean OnFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
Boolean result = false;
float diffY = e2.GetY() - e1.GetY();
float diffX = e2.GetX() - e1.GetX();
if (Math.Abs(diffX) > Math.Abs(diffY))
{
if (Math.Abs(diffX) > SWIPE_THRESHOLD && Math.Abs(velocityX) > SWIPE_VELOCITY_THRESHOLD)
{
if (diffX > 0)
{
SwipeRight();
}
else
{
SwipeLeft();
}
result = true;
}
}
else if (Math.Abs(diffY) > SWIPE_THRESHOLD && Math.Abs(velocityY) > SWIPE_VELOCITY_THRESHOLD)
{
if (diffY > 0)
{
SwipeBottom();
}
else
{
SwipeTop();
}
result = true;
}
return result;
}
public void SwipeRight()
{
this.OnSwipeRight?.Invoke(this, EventArgs.Empty);
}
public void SwipeLeft()
{
this.OnSwipeLeft?.Invoke(this, EventArgs.Empty);
}
public void SwipeTop()
{
this.OnSwipeTop?.Invoke(this, EventArgs.Empty);
}
public void SwipeBottom()
{
this.OnSwipeBottom?.Invoke(this, EventArgs.Empty);
}
}