简体   繁体   English

键值观察不会触发UIViewController子类?

[英]Key-Value Observing not triggering on a UIViewController subclass?

I'm trying to get notified when the title of a UIViewController changes. 我正试图在UIViewController的标题发生变化时收到通知。

I tried adding an observer to the title of a UIViewController subclass but it never gets triggered. 我尝试将一个观察者添加到UIViewController子类的标题,但它永远不会被触发。 What's strange about this, is that it works on a plain UIViewController. 这有什么奇怪的,它适用于普通的UIViewController。 Am I doing something wrong? 难道我做错了什么?

Here's a code example explaining my issue (Xamarin.iOS C#): 这是一个解释我的问题的代码示例(Xamarin.iOS C#):

using System;

using UIKit;
using System.Collections.Generic;

namespace ObserverTests
{
    public partial class ViewController : UIViewController
    {
        List<UIViewController> viewControllers = new List<UIViewController>();

        public override void ViewDidLoad()
        {
            UIViewController controller1 = new UIViewController() { Title = "Controller1" };
            UIViewController controller2 = new Test() { Title = "Controller2" };

            this.viewControllers.Add(controller1);
            this.viewControllers.Add(controller2);

            foreach(UIViewController viewController in viewControllers)
            {
                viewController.AddObserver("title", Foundation.NSKeyValueObservingOptions.New, (changes) =>
                    {
                        Console.WriteLine(viewController.Title);
                        Console.WriteLine("Title Changed!");
                    });
            }

            controller1.Title = "TitleChanged1"; // Works
            controller2.Title = "TitleChanged2"; // Fails
        }

        private class Test : UIViewController
        {
        }
    }
}

In Xamarin the best way might be using inheritance and adding such a feature. 在Xamarin中,最好的方法可能是使用继承并添加这样的功能。 For this you derive from UIViewController 为此,您从UIViewController派生

public class UIObserveTitleChangedViewController : UIViewController
{
    public event TitleChangedEvent TitleChanged;
    public override string Title
    {
        get
        {
            return base.Title;
        }

        set
        {
            var oldTitle = base.Title;
            if (oldTitle == value)
                return;
            base.Title = value;
            OnTitleChanged(new TitleChangedEventArgs(value, oldTitle));
        }
    }

    protected virtual void OnTitleChanged(TitleChangedEventArgs args)
    {
        TitleChanged?.Invoke(this, args);
    }

    #region ctor
    public UIObserveTitleChangedViewController() { }
    public UIObserveTitleChangedViewController(NSCoder coder) : base(coder) { }
    protected UIObserveTitleChangedViewController(NSObjectFlag t) : base(t) { }
    protected internal UIObserveTitleChangedViewController(IntPtr handle) : base(handle) { }
    public UIObserveTitleChangedViewController(string nibName, NSBundle bundle) : base(nibName, bundle) { }
    #endregion
}

and implement missing event types 并实现缺少的事件类型

public delegate void TitleChangedEvent(object sender, TitleChangedEventArgs args);

public class TitleChangedEventArgs : EventArgs
{
    public string NewTitle { get; set; }
    public string OldTitle { get; set; }

    public TitleChangedEventArgs(string newTitle, string oldTitle)
    {
        NewTitle = newTitle;
        OldTitle = oldTitle;
    }
}

You can then subscribe to this event and get notified of changes 然后,您可以订阅此事件并获得更改通知

public partial class ViewController : UIObserveTitleChangedViewController
{
    public ViewController(IntPtr handle) : base(handle)
    {
        this.TitleChanged += ViewController_TitleChanged; // Subscribe to TitleChanged
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        Title = "Some title";            // triggers TitleChanged
        Title = "Another new title";     // triggers TitleChanged
    }

    private void ViewController_TitleChanged(object sender, TitleChangedEventArgs args)
    {
        Debug.WriteLine("Title changed from {0} to {1}", args.OldTitle, args.NewTitle);
    }
}

The Title property in UIViewController is marked virtual ...so as an alternative solution, you could define a BaseViewController class and override Title and call a method in the Setter : UIViewControllerTitle属性标记为virtual ...作为替代解决方案,您可以定义BaseViewController类并override Title并在Setter调用方法:

Public class BaseViewController : UIViewController
{
    ....

    public override string Title {
    get {
        return base.Title;
    }
    set {
            base.Title = value;
            OnTitleChanged();
        }
    }

    protected virtual void OnTitleChanged()
    {
        ......
    }
}

Then you can override OnTitleChanged on any of your UIViewControllers to have a callback when the Title is changed. 然后,您可以在任何UIViewControllers上覆盖OnTitleChanged ,以便在更改Title时进行回调。

您可以像这样设置标题,它将起作用:

        controller2.SetValueForKey(new NSString("TitleChangedHAHA"), new NSString("title"));

You could do this. 你可以做到这一点。 First define a new event argument that will hold the new title when it changes. 首先定义一个新的事件参数,它将在更改时保存新标题。

public class TitleChangedEventArgs: EventArgs
{
    public string Title { get; private set; }

    public TitleChangedEventArgs(string title)
    {
        Title = title;
    }
}

In your test class, add a new Event Handler for TitleChanged and override Title to raise an event when the new title for the view controller. 在测试类中,为TitleChanged添加一个新的事件处理程序,并覆盖Title以在视图控制器的新标题时引发事件。

public class Test : UIViewController
    {
        public event EventHandler<TitleChangedEventArgs> TitleChanged;

        public override string Title {
            get {
                return base.Title;
            }
            set {
                base.Title = value;
                OnTitleChanged();
            }
        }

        public virtual void OnTitleChanged()
        {
            if (TitleChanged != null) { 
                TitleChanged.Invoke(this, EventArgs.Empty);
            }
        }
    }

and finally in your Main View Controller you can do something like this: 最后在您的主视图控制器中,您可以执行以下操作:

public class ViewController : UIViewController
    {
        private Test _test;

        public override void ViewDidLoad()
        {
            _test = new Test();
            base.ViewDidLoad();
        }

        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);
            _test.TitleChanged += Test_TitleChanged;
        }

        public override void ViewDidDisappear(bool animated)
        {
            _test.TitleChanged -= Test_TitleChanged;
            base.ViewDidDisappear(animated);
        }

        void Test_TitleChanged(object sender, TitleChangedEventArgs e)
        {
            Console.WriteLine("Title Changed! " + e.Title);
        }

        public override void ViewWillDisappear(bool animated)
        {
            base.ViewWillDisappear(animated);
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM