使用 MVVMCross 实现 UISearchController

Implementing a UISearchController using MVVMCross

我有一个应用程序,我正在使用 MVVMCross 从 iOS 仅转换为 iOS & Droid。

在我当前的应用程序中,我有一个使用 UISearchController 的地图视图,允许用户搜索附近的位置。这是基于 Xamarin 示例并且工作正常: Xamarin Map Example

对于转换我有:

  1. 绑定到 MapViewModel 的 MapView。
  2. 注入 MapViewModel 的搜索服务。
  3. 创建了 UISearchController 并将搜索文本绑定到 MapViewModel 上的 属性。

文本更新后,将调用搜索并检索结果。我正在努力解决的问题是如何将结果绑定回 UISearchController 提供的 SearchResultsView。

任何人都可以给我建议或指出正确的方向来解决这个问题。

我有下面的代码片段,可以让我了解到目前为止我所依赖的内容。

    [MvxFromStoryboard]
public partial class MapView : MvxViewController<MapViewModel>
{


    public MapView(IntPtr handle) : base(handle)
    {
    }

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

        var searchResultsController = new SearchResultsView();

        //Not sure if this is required
        //var searchUpdater.UpdateSearchResults += searchResultsController.Search;

        var searchController = new UISearchController(searchResultsController)
        {
            //Nore sure if this is required
            //SearchResultsUpdater = searchUpdater
        };


        searchController.SearchBar.SizeToFit();
        searchController.SearchBar.SearchBarStyle = UISearchBarStyle.Minimal;
        searchController.SearchBar.Placeholder = "Enter a search query";
        searchController.HidesNavigationBarDuringPresentation = false;
        DefinesPresentationContext = true;
        NavigationItem.TitleView = searchController.SearchBar;

        //Bind to View Model
        var set = this.CreateBindingSet<MapView, MapViewModel>();
        set.Bind(searchController.SearchBar).To(vm => vm.SearchQuery);
        set.Apply();
    }

}

public class SearchResultsUpdator : UISearchResultsUpdating
{
    public event Action<string> UpdateSearchResults = delegate { };

    public override void UpdateSearchResultsForSearchController(UISearchController searchController)
    {
        this.UpdateSearchResults(searchController.SearchBar.Text);
    }
}


[MvxFromStoryboard]
public partial class SearchResultsView : MvxTableViewController<SearchResultsViewModel>
{
    public SearchResultsView() { }

    public SearchResultsView(IntPtr handle) : base(handle)
    {
    }

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

        var source = new SearchResultsTableViewSource(TableView);
        TableView.Source = source;

        var set = this.CreateBindingSet<SearchResultsView, SearchResultsViewModel>();
        set.Bind(source).To(vm => vm.Results);
        set.Apply();


    }
}

[MvxFromStoryboard]
public partial class SearchResultsView : MvxTableViewController<SearchResultsViewModel>
{
    public SearchResultsView() { }

    public SearchResultsView(IntPtr handle) : base(handle)
    {
    }

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

        var source = new SearchResultsTableViewSource(TableView);
        TableView.Source = source;

        var set = this.CreateBindingSet<SearchResultsView, SearchResultsViewModel>();
        set.Bind(source).To(vm => vm.Results);
        set.Apply();


    }
}

我已经发布了这个以防其他人正在寻找一个例子。我决定最好的方法是让 iOS 处理结果的搜索视图控制器。代码如下。欢迎纠正或建议更好的选择

查看

    [MvxFromStoryboard]
    public partial class MapView : MvxViewController
    {
        UISearchController _searchController;
        SearchResultsViewController _searchResultsController;

        private IDisposable _searchResultsUpdateSubscription;
        private IMvxInteraction _searchResultsUpdatedInteraction;
        public IMvxInteraction SearchResultsUpdatedInteraction
        {
            get => _searchResultsUpdatedInteraction;
            set
            {
                if (_searchResultsUpdateSubscription != null)
                {
                    _searchResultsUpdateSubscription.Dispose();
                    _searchResultsUpdateSubscription = null;
                }

                _searchResultsUpdatedInteraction = value;
                if (_searchResultsUpdatedInteraction != null)
                {
                    _searchResultsUpdateSubscription = _searchResultsUpdatedInteraction.WeakSubscribe(OnSearchResultsUpdated);
                }
            }
        }


        private void OnSearchResultsUpdated(object sender, EventArgs e)
        {
            _searchResultsController.SearchResults = Results;
            _searchResultsController.ReloadSearchTable();
        }

        public List<Placemark> Results { get; set; }

        public MapView(IntPtr handle) : base(handle)
        {
        }

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

            //Bind to View Model
            var set = this.CreateBindingSet<MapView, MapViewModel>();
            set.Bind(_searchController.SearchBar).To(vm => vm.SearchQuery);
            set.Bind(this).For(v => v.Results).To(vm => vm.Results);
            set.Bind(this).For(v => v.SearchResultsUpdatedInteraction).To(vm => vm.SearchResultsUpdatedInteraction).OneWay();
            set.Apply();

        }

视图模型

    public class MapViewModel : MvxViewModel
    {
        readonly ILocationService _locationService;

        private MvxInteraction _searchResultsUpdatedInteraction = new MvxInteraction();
        public IMvxInteraction SearchResultsUpdatedInteraction => _searchResultsUpdatedInteraction;

        public MapViewModel(ILocationService locationService)
        {
            _locationService = locationService;
        }
        //***** Properties *****

        private List<Placemark> _results;
        public List<Placemark> Results
        {
            get => _results;
            set
            {
                _results = value;
                RaisePropertyChanged();
            }
        }

        private string _searchQuery;
        public string SearchQuery
        {
            get => _searchQuery;
            set
            {
                _searchQuery = value;
                //Task.Run(UpdateResultsAsync).Wait();
                RaisePropertyChanged();
                UpdateResultsAsync();
            }
        }

        //***** Privates *****
        private async Task UpdateResultsAsync()
        {
            Results = await _locationService.SearchForPlacesAsync(_searchQuery);
            _searchResultsUpdatedInteraction.Raise();
        }
    }

SearchResultsViewController

    public class SearchResultsViewController : UITableViewController
    {
        static readonly string mapItemCellId = "mapItemCellId";

        public List<Placemark> SearchResults { get; set; }

        public SearchResultsViewController()
        {

            SearchResults = new List<Placemark>();
        }

        public override nint RowsInSection(UITableView tableView, nint section)
        {
            return SearchResults == null ? 0 : SearchResults.Count;
        }

        public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
        {
            var cell = tableView.DequeueReusableCell(mapItemCellId);

            if (cell == null)
                cell = new UITableViewCell();

            cell.TextLabel.Text = SearchResults[indexPath.Row].FeatureName;
            return cell;
        }

        public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
        {
           //Do stuff here
        }

        public void ReloadSearchTable()
        {
            this.TableView.ReloadData();
        }
    }