何时在Xamarin Forms中初始化UITextView的边界和字体?

时间:2015-01-05 16:04:29

标签: xamarin.ios custom-controls xamarin.forms

我正在尝试为Xamarin.Forms项目实现带有提示文本功能的编辑器。这在Android中是微不足道的,因为底层的EntryEditText控件具有Hint属性。在iOS中,实现有点复杂,因为UITextView类没有实现提示文本。

我不喜欢这种技巧,"将文本设置为占位符,如果键入开始则清除它,如果键入结束则返回,文本为空白"。这意味着我必须做额外的工作来判断控件是否空白,并且有很多涉及文本颜色的摆弄。但是我一直有这么多麻烦我不得不诉诸它。也许有人可以帮助我。

我开始回答Placeholder in UITextView。我开始了一个新的Xamarin iOS项目,偶然发现了一个粗略的Obj-C到C#的转换,并且只需稍加改动就可以了:UITextView的Font属性还没有在构造函数中初始化,所以我不得不重写AwakeFromNib()设置占位符标签的字体。我测试了它并且它工作了,所以我将该文件带入Xamarin Forms项目,事情开始变得有点疯狂。

第一个问题是,显然MonoTouch在Xamarin Forms中有一些轻微的API差异,例如使用一些类型如RectangleF而不是CGRect。这很明显,如果不是出乎意料的话。在过去的几天里,我一直在努力解决其他一些差异,似乎无法以令我开心的方式克服它们。这是我的文件,因为我一直在尝试各种调试工作,因此显着减少了:

using System;
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using MonoTouch.CoreGraphics;
using System.Drawing;

namespace TestCustomRenderer.iOS {
    public class PlaceholderTextView : UITextView {

        private UILabel _placeholderLabel;
        private NSObject _notificationToken;

        private const double UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION = 0.25;

        private string _placeholder;
        public string Placeholder {
            get {
                return _placeholder;
            }
            set {
                _placeholder = value;
                if (_placeholderLabel != null) {
                    _placeholderLabel.Text = _placeholder;
                }
            }
        }

        public PlaceholderTextView() : base(RectangleF.Empty) {
            Initialize();
        }

        private void Initialize() {
            _notificationToken = NSNotificationCenter.DefaultCenter.AddObserver(TextDidChangeNotification, HandleTextChanged);
            _placeholderLabel = new UILabel(new RectangleF(8, 8, this.Bounds.Size.Width - 16, 25)) {
                LineBreakMode = UILineBreakMode.WordWrap,
                Lines = 1,
                BackgroundColor = UIColor.Green,
                TextColor = UIColor.Gray,
                Alpha = 1.0f,
                Text = Placeholder
            };

            AddSubview(_placeholderLabel);
            _placeholderLabel.SizeToFit();
            SendSubviewToBack(_placeholderLabel);
        }

        public override void DrawRect(RectangleF area, UIViewPrintFormatter formatter) {
            base.DrawRect(area, formatter);

            if (Text.Length == 0 && Placeholder.Length > 0) {
                _placeholderLabel.Alpha = 1;
            }
        }

        private void HandleTextChanged(NSNotification notification) {
            if (Placeholder.Length == 0) {
                return;
            }

            UIView.Animate(UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION, () => {
                if (Text.Length == 0) {
                    _placeholderLabel.Alpha = 1;
                } else {
                    _placeholderLabel.Alpha = 0;
                }
            });
        } 

        public override void AwakeFromNib() {
            base.AwakeFromNib();

            _placeholderLabel.Font = this.Font;
        }

        protected override void Dispose(bool disposing) {
            base.Dispose(disposing);

            if (disposing) {
                NSNotificationCenter.DefaultCenter.RemoveObserver(_notificationToken);
                _placeholderLabel.Dispose();
            }
        }

    }
}

这里一个值得注意的变化是将标签的初始化从DrawRect()重新定位到构造函数。据我所知,Xamarin永远不会让DrawRect()被调用。您还会注意到我没有设置Font属性。事实证明,在iOS MonoTouch项目中,有时父母的字体为空,将标签的字体设置为空也是非法的。在构建Xamarin设置字体之后的某个时刻,所以在AwakeFromNib()中设置该属性是安全的。

我编写了一个快速的编辑器派生类和一个自定义渲染器,因此Xamarin Forms可以渲染控件,渲染器稍微有点值得注意,因为我派生自NativeRenderer而不是EditorRenderer。我需要从重写的OnModelSet()调用SetNativeControl(),但是在程序集查看器上偷看显示,EditorRenderer会进行一些私人调用,我必须在我的内部重新实现。嘘。没有发布,因为这已经很大了,但我可以根据需要进行编辑。

上面的代码值得注意,因为占位符根本不可见。在面向iOS的MonoTouch中,您通常会使用框架初始化控件,并且调整大小是一种非常罕见的情况,您可以认为它不会发生。在Xamarin Forms中,布局由布局容器执行,因此构造函数提供的框架无关紧要。但是,标签的大小应在构造函数中设置,因此最终具有负宽度。糟糕。

我认为这可以通过将标签的实例化移动到AwakeFromNib()中来解决,或者至少在那里调整大小。这是因为我发现由于某种原因,在控件中没有调用AwakeFromNib()。韦尔普。我试图找到一个等效的回调/事件,这个回调/事件发生得足够晚,可以设置边界,但是在iOS端找不到任何东西。在尝试了很多很多东西之后,我注意到自定义渲染器收到了这个混乱的Xamarin Forms Model方面的属性更改事件。所以,如果我监听Height / Width更改事件,我可以调用标签上的方法,根据当前控件给它一个合理的大小。这暴露了另一个问题。

我找不到设置标签字体以匹配UITextView字体的方法。在构造函数中,Font属性为null。在iOS和Xamarin Forms项目中都是如此。在iOS项目中,当调用AwakeFromNib()时,属性被初始化并且一切都很好。在XF项目中,它从未被调用过,即使我从5秒延迟的任务中调用特技来调用方法(以确保显示控件),该属性仍然为空。

逻辑和iOS文档规定字体的默认值应为17点Helvetica。如果我捏造尺寸以使其可见,则占位符标签也是如此。 UITextView控件不是这样,但由于它将其字体报告为null,因此我无法看到字体实际是什么。如果我手动设置它一切都很好,当然,但我希望能够处理默认情况。这似乎是一个bug;这个盒子似乎在撒谎。我有一种感觉,它与Xamarin.Forms.Editor类没有Font属性的任何原因有关。

所以我正在寻找两个问题的答案:

  1. 如果我在XF中扩展iOS控件以添加子视图,那么处理子视图大小的最佳方法是什么?我发现高度/宽度更改会在渲染器中引发事件,这是唯一可行的方式吗?
  2. 当用户未设置该属性时,Xamarin Forms中的UITextView字体是否设置为非空值?我可以生活中要求此控件要求明确设置字体,但它很难吃,我想避免使用它。
  3. 我希望自己错过了一些明显的东西,因为我开始咆哮错误的树木。

1 个答案:

答案 0 :(得分:0)

  

如果我在XF中扩展iOS控件以添加子视图,那么它是什么   处理子视图大小的最佳方法?我找到了高度/宽度   更改会在渲染器中引发事件,这是唯一可行的方法吗?

这是我所知道的唯一方式,因为渲染器的曝光元素非常有限。

  

当用户未设置该属性时,是一个字体   Xamarin Forms中的UITextView是否设置为非null值?我可以活下去   要求此控件需要字体   明确设定,但它很难吃,我想避免它。

不,没有为Font指定默认的非空值。