I'm trying to write a CustomRenderer for iOS through which I want to change the BackgroundColor of the button when the user touches it. So far i got this:
public class BtnRendereriOS : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.BackgroundColor = UIColor.FromRGB(3, 169, 244);
Control.Layer.CornerRadius = 0;
}
Control.TouchUpInside += (sender, UIButton) => {
Control.BackgroundColor = UIColor.Brown;
};
}
}
Its not working however. I guess there needs to be some sort of eventhandler in order to make this possible.
The background colors needs to be initially set (if not the default color), then you need to set it back to that same color on TouchUpInside and TouchUpOutside. On TouchDown, set it to the color that you wish it to be while the button is being pressed.
Toggle background color:
if (Control != null)
{
void BackgroundNormalState(object sender)
{
(sender as UIButton).BackgroundColor = UIColor.Green;
}
BackgroundNormalState(Control);
Control.TouchUpInside += (object sender, EventArgs e) =>
{
BackgroundNormalState(sender);
};
Control.TouchUpOutside += (object sender, EventArgs e) =>
{
BackgroundNormalState(sender);
};
Control.TouchDown += (object sender, EventArgs e) =>
{
(sender as UIButton).BackgroundColor = UIColor.Red;
};
}
Update:
Can i change my textcolor through this methodgroup as well?
There are is a SetTitleColor where you can assign different colors to the different UIControlState values, Normal and Highlighted are the ones to start with:
Control.SetTitleColor(UIColor.Red, UIControlState.Normal);
Control.SetTitleColor(UIColor.Green, UIControlState.Highlighted);
Related
I have made a customrenderer for changing the TitleColor and BackgroundColor when an user click my button.
For some reason, when the button is focused, the TitleColor is turning more or less transparent, and my background doesn't animate like my TitleColor.
This is my code:
public class BtnRendereriOS : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Layer.CornerRadius = 0;
BackgroundNormalState(Control);
Control.TouchUpInside += (object sender, EventArgs c) =>
{
BackgroundNormalState(sender);
};
Control.TouchUpOutside += (object sender, EventArgs c) =>
{
BackgroundNormalState(sender);
};
Control.TouchDragOutside += (object sender, EventArgs c) =>
{
BackgroundNormalState(sender);
};
Control.TouchDragInside += (object sender, EventArgs c) =>
{
BackgroundChangedState(sender);
};
Control.TouchDown += (object sender, EventArgs c) =>
{
BackgroundChangedState(sender);
};
void BackgroundNormalState(object sender)
{
Action a = () => Control.SetTitleColor(UIColor.FromRGB(255,255,255), UIControlState.Normal);
UIView.PerformWithoutAnimation(a);
(sender as UIButton).BackgroundColor = UIColor.FromRGB(3, 169, 244);
}
void BackgroundChangedState(object sender)
{
Action a = () => Control.SetTitleColor(UIColor.FromRGB(0,0,0), UIControlState.Highlighted);
UIView.PerformWithoutAnimation(a);
(sender as UIButton).BackgroundColor = UIColor.FromRGB(104, 206, 253);
}
}
}
}
Currently, I have removed the animation from the TitleColor.
My questions are:
How do I add the same transition to my BackgroundColor similar to the default transition on my TitleColor?
How do I remove the "transparency" from the TitleColor when the button is being focused?
How do I remove the "transparency" from the TitleColor when the button
is being focused?
This is the feature of system button, if you don't like this try to use Custom style to avoid it:
UIButton btn = new UIButton(UIButtonType.Custom);
btn.SetTitle(Control.TitleLabel.Text, UIControlState.Normal);
SetNativeControl(btn);
// Pay attention to that the title color just needs to be set once
btn.SetTitleColor(UIColor.FromRGB(255, 255, 255), UIControlState.Normal);
btn.SetTitleColor(UIColor.FromRGB(0, 0, 0), UIControlState.Highlighted);
Then you can use UIView.Animate() to change your button's background color with "transition":
void BackgroundNormalState(object sender)
{
UIView.Animate(0.2, () =>
{
(sender as UIButton).BackgroundColor = UIColor.FromRGB(3, 169, 244);
});
}
void BackgroundChangedState(object sender)
{
UIView.Animate(0.2, () =>
{
(sender as UIButton).BackgroundColor = UIColor.FromRGB(104, 206, 253);
});
}
You can either use UIView.Animate|Async or UIView.TransitionNotify|Async depending upon the end result you are looking for. The key is to place all the changes within the same animation block so they occur at the same time via Core Animation (CA).
Here is an example using UIView.TransitionNotifyAsync using a TransitionCrossDissolve between the color changes.
// Set intial values
var normalTextColor = UIColor.FromRGB(255, 255, 255);
var normalBackgroundColor = UIColor.FromRGB(3, 169, 244);
var highlightTextColor = UIColor.Black;
var highlightBackgroundColor = UIColor.FromRGB(104, 206, 253);
Control.SetTitleColor(normalTextColor, UIControlState.Normal);
Control.BackgroundColor = normalBackgroundColor;
async Task NormalColorState(UIButton button)
{
await UIView.TransitionNotifyAsync(button, .25, UIViewAnimationOptions.TransitionCrossDissolve, () =>
{
button.BackgroundColor = normalBackgroundColor;
button.SetTitleColor(normalTextColor, UIControlState.Normal);
});
}
Control.TouchDown += async (object sender, EventArgs e) =>
{
var button = sender as UIButton;
await UIView.TransitionNotifyAsync(button, .25, UIViewAnimationOptions.TransitionCrossDissolve, () =>
{
button.BackgroundColor = highlightBackgroundColor;
button.SetTitleColor(highlightTextColor, UIControlState.Highlighted);
});
};
Control.TouchUpInside += async (object sender, EventArgs e) =>
{
await NormalColorState(sender as UIButton);
};
Control.TouchUpOutside += async (object sender, EventArgs e) =>
{
await NormalColorState(sender as UIButton);
};
I have a modal view, in which I have multiple Entry fields that I through an iOS customrenderer have customized to change BorderColor when Focused.
When i pop my modal view on button press:
await Navigation.PopModalAsync(true);
I get a nullreference in my iOS customrenderer, because I guess the element suddently becomes null, and i somehow haven't told it, that the view is gone.
public class BorderColorChange : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Layer.BorderWidth = 1;
Control.Layer.CornerRadius = 4;
e.NewElement.Focused += (sender, evt) =>
{
Control.Layer.BorderColor = UIColor.FromRGB(3, 169, 244).CGColor;
};
e.NewElement.Unfocused += (sender, evt) =>
{
Control.Layer.BorderColor = UIColor.LightGray.CGColor;
};
};
}
}
I've noticed, that when i remove the await keyword from Navigation.PopModalAsync(true); it doesn't produce the error.
Any help on how to solve this error?
It is perfectly normal for OnElementChanged to be called with e.NewElement==null. This just means that the element is being removed (like when you await the PopModelAsync), so it should handle the change that the new element to associate with is null.
With custom renderers, you need to both subscribe and unsubscribe to events when changes occur in associating your custom renderer with a native control. So for example:
public class BorderColorChange : EntryRenderer
{
private void MyFocusedEventHandler(...) ...
private void MyUnfocusedEventHandler(...) ...
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Layer.BorderWidth = 1;
Control.Layer.CornerRadius = 4;
if (e.OldElement != null) // unsubscribe from events on old element
{
e.OldElement.Focused -= MyFocusedEventHandler;
e.OldElement.Unfocused -= MyUnfocusedEventHandler;
}
if (e.NewElement != null) // subscribe to events on new element
{
e.NewElement.Focused += MyFocusedEventHandler;
e.NewElement.Unfocused += MyUnfocusedEventHandler;
}
}
}
}
The logic for what to do when the entry gets/loses focus goes into the MyFocusedEventHandler/MyUnfocusedEventHandler rather than inline to allow for both subscribing and unsubscribing.
My borderless custom renderer for picker
public class BorderlessPickerRenderer : PickerRenderer
{
public static void Init() { }
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
Control.Background = null;
}
}
}
It will change the picker list text color as white. please see the screenshot
If you check the source code of PickerRenderer, you will find that the Dialog is totally generated in the code behind.
So here to set a Transparent(border-less) background, we can re-write the Click event of this control, for example:
public class BorderlessPickerRenderer : Xamarin.Forms.Platform.Android.PickerRenderer
{
private IElementController ElementController => Element as IElementController;
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new NumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetDisplayedValues(model.Items.ToArray());
picker.WrapSelectorWheel = false;
picker.DescendantFocusability = DescendantFocusability.BlockDescendants;
picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton(global::Android.Resource.String.Cancel, (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
builder.SetPositiveButton(global::Android.Resource.String.Ok, (s, a) =>
{
ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, picker.Value);
// It is possible for the Content of the Page to be changed on SelectedIndexChanged.
// In this case, the Element & Control will no longer exist.
if (Element != null)
{
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
// It is also possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
};
_dialog.Show();
_dialog.Window.SetBackgroundDrawable(new ColorDrawable(Android.Graphics.Color.Transparent));
}
}
Rendering image of this custom picker:
The font color and button's style can be modified as you need since you created this dialog by yourself. And the style of the dialog also depends on the style of your app.
I' have some navigation page and I want to override the color for the back button and my next button ( ToolbarItem )
I Already tried BarTextColor property but it change color for all navigation header text.
It's done in IOS, but I' not able to find a solution for android.
It works perfectly for the title but not for the Icons.
Here my code :
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var page = this.Element as NavigationPage;
if (page != null && toolbar != null)
{
toolbar.SetTitleTextColor(Color.Black.ToAndroid());
if (toolbar.NavigationIcon != null)
toolbar.NavigationIcon.SetColorFilter(Color.Green.ToAndroid(), Android.Graphics.PorterDuff.Mode.Multiply);
if (toolbar.OverflowIcon != null)
toolbar.OverflowIcon.SetColorFilter(Color.Green.ToAndroid(), Android.Graphics.PorterDuff.Mode.Multiply);
}
}
I' have some navigation page and I want to override the color for the back button and my next button ( ToolbarItem )
Your next button is a ToolbarItem, which is defined by yourself. So it won't be a problem for you to customize it. The difficult part lies in the back button, because it is offered by Xamarin.Forms. You need to override the NavigationPageRenderer to change the color:
public class MyNavigationPageRenderer : NavigationPageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<NavigationPage> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var navController = (INavigationPageController)e.NewElement;
navController.PushRequested += NavController_PushRequested;
navController.PopRequested += NavController_PopRequested;
}
}
private void NavController_PopRequested(object sender, Xamarin.Forms.Internals.NavigationRequestedEventArgs e)
{
Device.StartTimer(TimeSpan.FromMilliseconds(220), () =>
{
ChangeIconColor();
return false;
});
}
private void NavController_PushRequested(object sender, Xamarin.Forms.Internals.NavigationRequestedEventArgs e)
{
ChangeIconColor();
}
private void ChangeIconColor()
{
int count = this.ViewGroup.ChildCount;
var toolbar = GetToolbar();
if (toolbar.NavigationIcon != null)
{
var drawable = (toolbar.NavigationIcon as DrawerArrowDrawable);
drawable.Color = Resource.Color.material_grey_850;//set the navigation icon color here
}
}
private AToolbar GetToolbar()
{
for (int i = 0; i < this.ViewGroup.ChildCount; i++)
{
var child = this.ViewGroup.GetChildAt(i);
if (child is AToolbar)
{
return (AToolbar)child;
}
}
return null;
}
}
A little explanation to the codes above: PushRequest and PopRequest fires when you push and pop a new page to the navigation page and it is the perfect time for you to customize the existing Toolbar's NavigationIcon. So first find the Toolbar using GetToolbar then change the icon color by ChangeIconColor.
How can I make the pin to display label by default (without clicking it) when it is added to the map in Xamarin.Forms.
map.MoveToRegion(MapSpan.FromCenterAndRadius(position, Distance.FromMiles(0.4)));
var pin = new Pin
{
Type = PinType.Place,
Position = position,
Label = "Some Text",
};
map.Pins.Add(pin);
You can do it via a custom map render.
As an example, on iOS you can add two delegates to the MKMapView control:
DidAddAnnotationViews: Any time a MKAnnotation is added, pre-select them all..
DidDeselectAnnotationView: If someone/something tries to deselect the MKAnnotation, just re-select them all...
Working Example as a starting point:
[assembly: ExportRenderer(typeof(PinViewMap), typeof(PinViewMapRenderer))]
namespace WorkingWithMaps.iOS
{
public class PinViewMapRenderer : MapRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var map = Control as MKMapView;
map.DidDeselectAnnotationView += (object sender, MKAnnotationViewEventArgs eventArgs) =>
{
foreach (var anno in ((MKMapView)sender).Annotations)
{
((MKMapView)sender).SelectAnnotation(anno, true);
}
};
map.DidAddAnnotationViews += (object sender, MKMapViewAnnotationEventArgs eventArgs) =>
{
foreach (var anno in ((MKMapView)sender).Annotations)
{
((MKMapView)sender).SelectAnnotation(anno, true);
}
};
}
}
}
}