I would like to change the title of the cancel button of the UISearchBar using MonoTouch.
Yes I have seen this "hack" but I prefer a more elegant way. I am trying to use:
UIButton.AppearanceWhenContainedIn(typeof(UISearchBar)).SetTitle("xxxx");
which it is supposed to work in objective-c but in MonoTouch I get the following error
message: Error CS1061: Type
MonoTouch.UIKit.UIButton.UIButtonAppearance' does not contain a
definition forSetTitle' and no extension method SetTitle' of type
MonoTouch.UIKit.UIButton.UIButtonAppearance' could be found (are you
missing a using directive or an assembly reference?)
I am using Xamarin Studio ver. 4.0
It's hackish - the Objective-C code works because the current implementation of UIAppearance use a proxy object. IOW Apple did not promise to keep the implementation identical and that specific feature is not documented.
Now that you're aware of this and if you still want to use it with Xamarin.iOS then you can do it using the hack I described in this Q&A. That will allow you to use any UIButton API on the UIAppearance proxy.
Why not use a bit more lightweight hack?
public static void SetSearchBarCancelButtonTitle (UISearchBar sb, string title)
{
foreach (var subview in sb.Subviews)
if (subview is UIButton)
(subview as UIButton).SetTitle (title, UIControlState.Normal);
}
UIButton no longer appears to be a direct subView of UISearchBar. You'll have to recurse like the following, until it is found:
public static bool SetButton(UIView parentView, string title, UIColor tint)
{
foreach (var sub in parentView.Subviews)
{
if (sub is UIButton)
{
(sub as UIButton).SetTitle(title, UIControlState.Normal);
(sub as UIButton).SetTitleColor(tint, UIControlState.Normal);
return true;
}
if (SetButton(sub, title, tint))
return true;
}
return false;
}
Related
I'm pretty new to iOS development and I was asking my self if it is possible to use localized strings from my "Localizable.strings" file directly into the storyboard.
For example in Android you can do it from the XML file like this:
android:text="#string/notConnected"
I understood that you can make a localized version of the storyboard, but having different strings files and different storyboards looks pretty ugly to me.
So is it possible to have only strings files and use what I need into the storyboard? Preferably without setting it from code?
EDIT:
This is practically what I want to do:
So is this possible? Is there a legit way to call a string from there like in Android?
I think being able to localise Strings in the storyboard is of significant advantage. I don't agree with #elk_cloner that hooking up IBOutlets for every UILabel is the way forward.
One way of getting it to work is using an #IBInspectable property on a UILabel subclass:
class LocalisableLabel: UILabel {
#IBInspectable var localisedKey: String? {
didSet {
guard let key = localisedKey else { return }
text = NSLocalizedString(key, comment: "")
}
}
}
In the storyboard set the custom class:
In the attributes inspector the localisedKey field will appear and you can just add your key here.
That's it!
EDIT:
You can localise UIButtons the same way, BUT if the text in the storyboard's title field differs from the localised String (which it will in other languages) the setting of the title will animate.
To fix this, put the setTitle in a performWithoutAnimation block:
class LocalisableButton: UIButton {
#IBInspectable var localisedKey: String? {
didSet {
guard let key = localisedKey else { return }
UIView.performWithoutAnimation {
setTitle(key.localized, for: .normal)
layoutIfNeeded()
}
}
}
}
In addition to Leon's answer, you can get rid of the need for an explicit subclass by using an extension:
extension UILabel {
#IBInspectable var localizableText: String? {
get { return text }
set(value) { text = NSLocalizedString(value!, comment: "") }
}
}
According to your requirement it's not possible but
You don't need different storyboards for localization
Suppose you want to localize a label string.
Draw and outlet and change text using
mylabel.text = nsLocalizedString("THIS_IS_MY_STRING",nil);
Of course in your localization file there will be a line.You must have different files for different language.Suppose you have a file for english and there must be a line.
"THIS_IS_MY_STRING" = "This is my string";
When you compile your app, that function will use mapping to localize your app.
Edit:
If you want detail information please have a look at these tutorials
internationalization-tutorial-for-ios-2014
and ios-localization-tutorial
There are some online script(e.g localize.py) which will help you to automatically search all of your code and find out nslocalizedString function and make lines in your localizableString files. like this.
"THIS_IS_MY_STRING" = "THIS_IS_MY_STRING"
and later on you just have to write actual string there. :)
Make a button class and set #IBInspectable attribute to the button class
class Button: UIButton {
#IBInspectable public var referenceText: String = "" {
didSet {
self.setTitle(NSLocalizedString(referenceText, comment: ""), for: .normal)
}
}
}
Then in the storyboard you can set referenceText
The button text will be "Sign In"
Another possible way is to localize the storyboard and simply change the values for labels and buttons directly on the .string file.
First select the storyboard, and click on localize:
Then you'll be able to select the languages you want.
This way you'll be able to continue developing on your language of choice and simply edit the .string file that is generated
for button
extension UIButton {
#IBInspectable var localizableText: String? {
get { return titleLabel?.text }
set(value)
{
setTitle(NSLocalizedString(value!, comment: ""), for: .normal)
}}}
I have a delared the following
public class ViewBase : ContentView
{
//...
}
When I use that in XAML
<local:ViewBase xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyForms;"
x:Class="MyForms.Finder"
BackgroundColor="Color.Yellow">
<!-- ... -->
</local:ViewBase>
When I use a CustomRenderer for that and even (as below) do nothing in it, the BackgroundColor from above is not set. When I don't define the following lines the background is yellow as expected.
[assembly: ExportRenderer(typeof(ViewBase), typeof(ViewRendererBase))]
namespace MyiOS
{
public class ViewRendererBase : ViewRenderer
{
}
}
BackgroundColor is a Property of ViewRenderer. I had a look into the code, it seems as Control is no set (I don't call SetNativeControl) it can't set Control.BackgroundColor to a value. But why does this happen? My guess is that something is wrong with the inheritance of ViewRenderer, as the default behaviour uses something different on ContentView!?
Not sure whether this is a bug in our docs [1] or a bug in the iOS ViewRenderer code [2] for the SetBackgroundColor method. So there are a couple of ways to workaround this. One is to have your custom renderer inherit from VisualElementRenderer<T> instead, e.g.:
public class ViewBaseRenderer : VisualElementRenderer<ContentView>
{
//...
}
When checking on the default renderer type in iOS code with:
var contentRenderer = Platform.CreateRenderer(new ContentView())
var rendererType = contentRenderer.GetType();
rendererType is a VisualElementRenderer<T>, so this would seem to be the default renderer that is used by Forms, so it would seem to be a bug in the docs.
Another "workaround" would be to use the ViewRenderer but override the SetBackgroundColor method:
public class ViewBaseRenderer : ViewRenderer
{
protected override void SetBackgroundColor(Color color)
{
base.SetBackgroundColor(color);
if (NativeView == null)
return;
if (color != Color.Default)
NativeView.BackgroundColor = color.ToUIColor();
}
I have brought this up with the Xamarin Forms team to determine whether it is a bug in the docs or a bug in the Forms iOS Platform code for the ViewRenderer. If you look at the forms source code I linked [2] you will see that if Control is null, which it is in this case, the background color is never set. By overriding and adding the code to set the background color for NativeView then you can workaround this possible bug.
So apparently it seems the docs are in error. If you use ViewRenderer you have to set the Control yourself. Other renderers that inherit from ViewRenderer, like LabelRender, set the Control already, but ViewRenderer does not, so you would have to call SetNativeControl() in the OnElementChanged override to create and set a native control. See this forum post [3]. Personally, I think that one should inherit from the renderer that is used by default, which in this case is a VisualElementRenderer<T>
[1] https://developer.xamarin.com/guides/xamarin-forms/custom-renderer/renderers/#Layouts
[2] https://github.com/xamarin/Xamarin.Forms/blob/74cb5c4a97dcb123eb471f6b1dffa1267d0305aa/Xamarin.Forms.Platform.iOS/ViewRenderer.cs#L99
[3] https://forums.xamarin.com/discussion/comment/180839#Comment_180839
I would really like to be able to use the "User Defined Runtime Attributes " from xcode Storyboard to build a nice pop up through a container view.
Unfortunately, I can't make it works and can't figure out why !
I found many topics (eg: Is it possible to set UIView border properties from interface builder?) which deal about it but this doesn't work for me... !
Here is the attribute inspector of the containerView embed UIView (I also tried to implement into containerView UIView too with no success).
I added an extension to transform UIColor to CGColor as expected :
extension CALayer {
var borderUIColor: UIColor {
set {
self.borderColor = newValue.CGColor
}
get {
return UIColor(CGColor: self.borderColor!)
}
}
}
Does someone could think about something missing ?
Thank you very much in advance ;)
Instead of layer.borderColor, use layer.borderUIColor in your user defined runtime attributes. Just double click the key name and add UI.
I'm trying to be clever about setting all title properties of the the "Back" buttons in a UINavigationController so that I don't have to do self.navigationController.navigationBar.backButtonItem.title = "Back" everywhere or subclass a UINavigationController and set it everywhere, so I've created this extension:
extension UINavigationItem {
open var backBarButtonItem: UIBarButtonItem? {
get {
return self.backBarButtonItem
}
set {
newValue?.title = "Back"
backBarButtonItem = newValue?
}
}
}
But it says 'backBarButtonItem' used within its own type.
Has anybody done this before or can think of a way to make it work?
You are getting this error because you cannot create a variable with the name which is similar to those variables which are defined in the SDK.
You can't override the existing functionality
Like in your case you are naming it as backBarButtonTitle which is defined as open var backBarButtonItem: UIBarButtonItem? in UINavigationBar class of UIKit
As it is mentioned in doc of Apple
Extensions can add new functionality to a type, but they cannot
override existing functionality.
Please follow this Screen shot Image then run your project . I think you can solved your problem easily :)
I need to prevent users from entering a caret ("^") into a notes field that is implemented in a UITextView. I found this question: prevent lower case in UITextView, but it's not clear to me when/how often the shouldChangeTextInRange method will be called. Is it called for each keystroke? Is it named this way because it will be called once for a paste? Instead of preventing the entire paste operation, I'd rather strip out the offending carets, which it doesn't look like that method can do.
Our main application (written in C++Builder with VCL components) can filter keystrokes, so that if ^ is pressed, it beeps and the character is not added to the text field. I would like to replicate that behavior here.
Is there any way to do that sanely in Xamarin? I'm doing iOS first, and might be asking about Android later.
Thanks for your help!
Are you using Xamarin.Forms to build your UI? If you're going to be targeting Android, I highly recommend doing so.
If that is the case, then you can easily do this with a custom Entry subclass:
public class FilteredEntry : Entry
{
private string FilterRegex { get; set; }
public FilteredEntry (string filterRegex)
{
// if we received some regex, apply it
if (!String.IsNullOrEmpty (filterRegex)) {
base.TextChanged += EntryTextChanged;
FilterRegex = filterRegex;
}
}
void EntryTextChanged (object sender, TextChangedEventArgs e)
{
string newText = e.NewTextValue;
(sender as Entry).Text = Regex.Replace (newText, FilterRegex, String.Empty);
}
}
Usage:
// The root page of your application
MainPage = new ContentPage {
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
Children = {
new FilteredEntry(#"\^")
}
}
};
A typed ^ will be stripped out of the Entry's Text.