MVVMCross iOS: How to trigger RowSelected on a Custom TableViewSource using CreateBindingSet? - mvvm

My question is for MVVMCross in iOS. I have a custom TableView and a detailView. How can I bind my "showDetailCommand", so when user click on one of the rows on TableView with trigger RowSelected to switch to detailViewModel?
Here is my code structure:
public class SearchResultsViewModel: MvxViewModel
{
private MvxCommand _showDetailCommand;
public IMvxCommand ShowDetailCommand
{
get
{
_showDetailCommand = _showDetailCommand ?? new MvxCommand(ShowDetailCommandHandler);
return _showMapCommand;
}
}
private void ShowDetailCommandHandler()
{
ShowViewModel<ResultDetailViewModel>(new
{
city = _filter.City,
state = _filter.State,
interstate = _filter.Interstate,
travelPlaza = _filter.SearchTravelPlaza,
hasCatScale = _filter.HasCatScale,
hasWashoutExpress = _filter.HasWashoutExpress,
hasUndercarriage = _filter.HasUndercarriage,
nearest = _nearest
});
}
}
[Register("SearchResults")]
public class SearchResultsView : MvxTableViewController
{
public override void ViewDidLoad()
{
Title = "List";
base.ViewDidLoad();
var source = new TableViewSource(TableView);
var bindings = this.CreateBindingSet<SearchResultsView, SearchResultsViewModel>();
bindings.Bind(source).To(vm => vm.Items);
bindings.Apply();
TableView.BackgroundColor = UIColor.Clear;
TableView.ShowsVerticalScrollIndicator = false;
TableView.ScrollEnabled = true;
TableView.SeparatorStyle = UITableViewCellSeparatorStyle.None;
TableView.Source = source;
TableView.ReloadData();
}
public class TableViewSource : MvxTableViewSource
{
public TableViewSource(UITableView tableView)
: base(tableView)
{
tableView.RegisterClassForCellReuse(typeof(TableViewCell), TableViewCell.CellIdentifier);
}
public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
var item = GetItemAt(indexPath);
return TableViewCell.CellHeight(item);
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
var cell = tableView.DequeueReusableCell(TableViewCell.CellIdentifier) ?? new TableViewCell();
return cell;
}
}
}
[Register("TableViewCell")]
public class TableViewCell : MvxTableViewCell
{
public static readonly NSString CellIdentifier = new NSString("TableViewCell");
public TableViewCell()
: base()
{
Initialise();
}
public TableViewCell(IntPtr handle)
: base(handle)
{
Initialise();
}
private void Initialise()
{
var titleLabel = new UILabel(new RectangleF(10, 2, 200, 25));
Add(titleLabel);
this.DelayBind(() =>
{
var bindings = this.CreateBindingSet<TableViewCell, SearchResultsItemViewModel>();
bindings.Bind(titleLabel).For(x => x.Text).To(vm => vm.Name);
bindings.Bind(??).For(x => x.SelectionChangeCommand).To(vm => vm.showDetailCommand); // what should be in this line ??
bindings.Apply();
});
}
}

You can bind a view model command to the SelectionChangedCommand on the table source.
This technique is demonstrated in several samples - e.g
N=27 http://www.youtube.com/watch?v=h0Eww89c9DM&feature=youtu.be&t=47m30s
https://github.com/slodge/MvvmCross-Tutorials/tree/master/DailyDilbert
// command
new Cirrious.MvvmCross.ViewModels.MvxCommand<DilbertItem>((dilbertItem) =>
{
// note that we can only pass an id here - do *not* serialiase the whole dilbertItem
ShowViewModel<DilbertDetailViewModel>(new { id = dilbertItem.Id });
});
// binding
set.Bind(source)
.For(s => s.SelectionChangedCommand)
.To(s => s.ShowDetailCommand);
Alternatively, you can also put commands inside your list items and can then bind within each cell to a SelectedCommand (or some other custom event within your cell).
For examples of this, look at some of the samples which have menus - e.g. the ValueConversion sample

Related

How to geocode destination point?

I am trying to navigation application on Mapbox with Unity. I can take the location of the user but i could not able to take destination point from the user. I want to take as text. If i can able to take destination point, i can plot the route easily.
I tried the reload map, but it is not useful. Also, i tried the forward geocode user input file, but i could not make it.
namespace Mapbox.Examples
{
using Mapbox.Unity;
using UnityEngine;
using UnityEngine.UI;
using System;
using Mapbox.Geocoding;
using Mapbox.Utils;
[RequireComponent(typeof(InputField))]
public class ForwardGeocodeUserInput : MonoBehaviour
{
InputField _inputField;
ForwardGeocodeResource _resource;
Vector2d _coordinate;
public Vector2d Coordinate
{
get
{
return _coordinate;
}
}
bool _hasResponse;
public bool HasResponse
{
get
{
return _hasResponse;
}
}
public ForwardGeocodeResponse Response { get; private set; }
//public event Action<> OnGeocoderResponse = delegate { };
public event Action<ForwardGeocodeResponse> OnGeocoderResponse = delegate { };
void Awake()
{
_inputField = GetComponent<InputField>();
_inputField.onEndEdit.AddListener(HandleUserInput);
_resource = new ForwardGeocodeResource("");
}
void HandleUserInput(string searchString)
{
_hasResponse = false;
if (!string.IsNullOrEmpty(searchString))
{
_resource.Query = searchString;
MapboxAccess.Instance.Geocoder.Geocode(_resource, HandleGeocoderResponse);
}
}
void HandleGeocoderResponse(ForwardGeocodeResponse res)
{
_hasResponse = true;
if (null == res)
{
_inputField.text = "no geocode response";
}
else if (null != res.Features && res.Features.Count > 0)
{
var center = res.Features[0].Center;
//_inputField.text = string.Format("{0},{1}", center.x, center.y);
_coordinate = res.Features[0].Center;
}
Response = res;
OnGeocoderResponse(res);
}
}
}
Could you help me?

How can I use Swift protocol function like as Android interface listener implement?

I'm Korean android developer and new at Swift.
I am migrating my android app to ios, but meet a problem with interface and listener. I don't know how to implement listener to communicate between a custom view and a view controller.
I have a custom view(i.e. MyView) that has two buttons and each has own function.
In Android(with Java), I usually make an listener Interface in MyView and assign two functions inside like void func1( String val1 ) and func2...
public class MyView extends RelativeLayout {
private Button b1, b2;
private String val1, val2;
public interface OnButtonListener {
void onFunc1(String val1);
void onFunc2(String val2);
}
private OnButtonListener onButtonListener;
public void setOnButtonListener( OnButtonListener onButtonListener ) {
this.onButtonListener = onButtonListener;
}
public MyView( Context context, AttributeSet attrs ) {
super( context, attrs );
b1.setOnClickListener( view -> {
if (onButtonListener != null){
onButtonListener.onFunc1( val1 );
}
} );
b2.setOnClickListener( view -> {
if (onButtonListener != null){
onButtonListener.onFunc1( val2 );
}
} );
}
}
public class MyActivity extends Activity {
MyView myView1, myView2;
#Override
protected void onCreate( #Nullable Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
myView1.setOnButtonListener( new MyView.OnButtonListener() {
#Override
public void onFunc1( String val1 ) {
Log.d(TAG, val1);
}
#Override
public void onFunc2( String val2 ) {
Log.d(TAG, val2);
}
} );
myView2.setOnButtonListener( new MyView.OnButtonListener() {
#Override
public void onFunc1( String val1 ) {
// do something1
}
#Override
public void onFunc2( String val2 ) {
// do something2
}
} );
}
}
This code works perfectly as I wanted. So I've tried to apply same pattern into Swift, but I couldn't find any way to do.
below is for swift 4
import Foundation
import UIKit
import SnapKit
protocol OnButtonListener {
func onButton1( _ val1: String )
func onButton2( _ val2: String )
}
class MyView: UIView {
var onButtonListener: OnButtonListener?
var val1 = "abc"
var val2 = "123"
override init( frame: CGRect ) {
super.init( frame: frame )
let b1 = UIButton()
let b2 = UIButton() // i'm using snapkit
b1.addTarget(self, action: #selector(onB1), for: .touchUpInside)
b2.addTarget(self, action: #selector(onB2), for: .touchUpInside)
}
#objc func onB1() {
if onButtonListener != nil {
onButtonListener!.onButton1(val1 )
}
}
#objc func onB2() {
if onButtonListener != nil {
onButtonListener!.onButton2(val2 )
}
}
}
class MyVC : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let myView1 = MyView()
let myView2 = MyView()
myView1.onButtonListener = {
// ???
}
myView2.onButtonListener = {
// ???
}
}
}
I don't know how to implement listener in ViewContorller. I've tried same way as Kotlin but I didn't work too. Thank you for reading.
You have to set delegate in your viewcontroller and implement protocol methods in your viewcontroller
class MyVC : UIViewController, OnButtonListener {
override func viewDidLoad() {
super.viewDidLoad()
let myView1 = MyView()
myView1. onButtonListener = self
let myView2 = MyView()
myView2. onButtonListener = self
}
func onButton1( _ val1: String ) {
print(val1)
}
func onButton2( _ val2: String ) {
print(val2)
}
}
**Method 2: ** You can use block as well
class MyView: UIView {
var buttonAction : ((_ value : String) -> Void)? = nil
//.... your code
#objc func onB1() {
if let action = buttonAction {
action("abc")
}
}
#objc func onB2() {
if let action = buttonAction {
action("xyz")
}
}
}
In you ViewController
override func viewDidLoad() {
super.viewDidLoad()
let myView1 = MyView()
myView1.buttonAction = { value in
print(value)
}
}
Update your view controller code as follows:
First confirm your OnButtonListener protocol to UIViewController, and implement protocol method in your view controller.
class MyVC : UIViewController, OnButtonListener {
override func viewDidLoad() {
super.viewDidLoad()
let myView1 = MyView()
// Confirm protocol implementation in current view controller
myView1.onButtonListener = self
let myView2 = MyView()
// Confirm protocol implementation in current view controller
myView2.onButtonListener = self
}
func onButton1( _ val1: String) {
// your code
}
func onButton2( _ val2: String) {
// your code
}
}

use tableview as searchable input

I have this tableview that is searcheable. at this point I can fill it and on search goes to a detailview. know I want to convert this to use it as an input? but how?
I have a view with the input, and I technically have a tableview that returns the value, just not in the correct manner.
tableviewmodel:
public class CMCTableViewModel : MvxViewModel
{
protected readonly ICoinMarketCapService _coinMarketCapService;
public CMCTableViewModel(ICoinMarketCapService coinMarketCapService)
{
_coinMarketCapService = coinMarketCapService;
LoadData();
}
public MvxCommand<CoinMarketCapModel> NavigateToDetailCommand
{
get
{
return new MvxCommand<CoinMarketCapModel>(
SelectedCoin =>
{
ShowViewModel<CoinViewModel>(new { coinName = SelectedCoin.Id });
}
);
}
}
private List<CoinMarketCapModel> _coinMarketCapModelList;
private CoinMarketGlobData _CoinMarketGlobDataList;
public List<CoinMarketCapModel> CoinMarketCapModelList
{
get
{
return _coinMarketCapModelList;
}
set
{
_coinMarketCapModelList = value;
RaisePropertyChanged(() => CoinMarketCapModelList);
}
}
public CoinMarketGlobData CoinMarketGlobDatas
{
get
{
return _CoinMarketGlobDataList;
}
set
{
_CoinMarketGlobDataList = value;
RaisePropertyChanged(() => CoinMarketGlobDatas);
}
}
public async void LoadData()
{
//CoinMarketCapModelList = await _coinMarketCapService.GetCoins("20");
//CoinMarketGlobDatas = await _coinMarketCapService.GetGlobalData();
CoinMarketCapModelList = await _coinMarketCapService.GetCoins();
FilteredList = CoinMarketCapModelList;
}
private List<CoinMarketCapModel> _FilteredList;
public List<CoinMarketCapModel> FilteredList
{
get
{
return _FilteredList;
}
set
{
_FilteredList = value;
RaisePropertyChanged(() => FilteredList);
}
}
public void SearchByText(string text)
{
if (string.IsNullOrWhiteSpace(text))
FilteredList = CoinMarketCapModelList;
else
{
FilteredList = CoinMarketCapModelList;
FilteredList = FilteredList.Where(m => m.Name.ToLowerInvariant().Contains(text.ToLowerInvariant())).ToList();
}
}
/// <summary>
/// Gets or sets the subtitle for the base model
/// </summary>
}
View:
[MvxFromStoryboard(StoryboardName = "Main")]
public partial class CMCTableView : MvxTableViewController<CMCTableViewModel>
{
bool useRefreshControl = false;
//private UIRefreshControl refreshControl;
private MvxUIRefreshControl refreshControl;
private void refreshTable(object sender, EventArgs e)
{
refreshControl.EndRefreshing();
TableView.ReloadData();
}
public CMCTableView (IntPtr handle) : base (handle)
{
}
UISearchBar _searchBar;
CMCTableViewSource _cmcTableViewSource;
public override void ViewDidLoad()
{
refreshControl = new MvxUIRefreshControl();
refreshControl.ValueChanged += refreshTable;
TableView.AddSubview(refreshControl);
_cmcTableViewSource = new CMCTableViewSource(this.TableView);
base.ViewDidLoad();
this.TableView.Source = _cmcTableViewSource;
this.TableView.ReloadData();
//BEGIN initialize searchbar
var searchController = new UISearchController(searchResultsController: null);
searchController.SearchBar.SizeToFit();
searchController.SearchBar.SearchBarStyle = UISearchBarStyle.Minimal;
searchController.SearchBar.Placeholder = "Select a currency";
searchController.DimsBackgroundDuringPresentation = false;
NavigationItem.HidesSearchBarWhenScrolling = false;
NavigationItem.SearchController = searchController;
_searchBar = searchController.SearchBar;
_searchBar.SearchButtonClicked += SearchBar_SearchButtonClicked;
_searchBar.TextChanged += SearchBarOnTextChanged;
_searchBar.CancelButtonClicked += SearchBarOnCancelButtonClicked;
// END initialize searchbar
MvxFluentBindingDescriptionSet<CMCTableView, CMCTableViewModel> set = new MvxFluentBindingDescriptionSet<CMCTableView, CMCTableViewModel>(this);
set.Bind(_cmcTableViewSource).To(vm => vm.FilteredList);
set.Bind(_cmcTableViewSource)
.For(src => src.SelectionChangedCommand)
.To(vm => vm.NavigateToDetailCommand);
set.Apply();
}
private void SearchBarOnCancelButtonClicked(object sender, EventArgs eventArgs)
{
((CMCTableViewModel)ViewModel).SearchByText(string.Empty);
BeginInvokeOnMainThread(() => _searchBar.ResignFirstResponder());
}
//public override void ViewWillAppear(bool animated)
//{
// base.ViewWillAppear(animated);
// NavigationController.NavigationBarHidden = false;
//}
private void SearchBarOnTextChanged(object sender, UISearchBarTextChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(_searchBar.Text))
{
((CMCTableViewModel)ViewModel).SearchByText(string.Empty);
BeginInvokeOnMainThread(() => _searchBar.ResignFirstResponder());
}
else
{
((CMCTableViewModel)ViewModel).SearchByText(_searchBar.Text);
}
}
private void SearchBar_SearchButtonClicked(object sender, EventArgs e)
{
((CMCTableViewModel)ViewModel).SearchByText(_searchBar.Text);
BeginInvokeOnMainThread(() => _searchBar.ResignFirstResponder());
}
}
i used a button as input that goes to the tableview, on selection i store the needed data(id and name in this case) in a static model. then i return to the parentview.
model:
public class addUserCoinHelperModel
{
public static string coinID { get; set; }
public static string name { get; set; }
public static string Amount { get; set; }
}
on selection in viewmodel:
public MvxCommand<CoinMarketCapModel> NavigateToDetailCommand
{
get
{
return new MvxCommand<CoinMarketCapModel>(
SelectedCoin =>
{
addUserCoinHelperModel.coinID = SelectedCoin.Id;
addUserCoinHelperModel.name = SelectedCoin.Name;
ShowViewModel<AddUserCoinViewModel>();
//ShowViewModel<CoinViewModel>(new { coinName = SelectedCoin.Id });
}
);
}
}
in the view in the viewdidload:
if (addUserCoinHelperModel.coinID != null){
btnPickCoin.SetTitle(addUserCoinHelperModel.name, UIControlState.Normal);
}

Swift: class and properties - all only initialized once

I'm looking to have a class or struct (does not matter) with properties, where everything may only be initialized once in the app. Any attempt to modify the class or its properties will fail or not be possible.
Is this possible?
I've come up with this so far:
public struct ScreenInfo
{
static var scaleFactor:Int = 0
public init(initialScaleFactor:Int)
{
if (ScreenInfo.scaleFactor == 0) {
ScreenInfo.scaleFactor = initialScaleFactor
}
}
public static func getScaleFactor()
-> Int
{
return scaleFactor
}
}
let si1:ScreenInfo = ScreenInfo(initialScaleFactor:11)
ScreenInfo.getScaleFactor() // = 11
let si2:ScreenInfo = ScreenInfo(initialScaleFactor:22)
ScreenInfo.getScaleFactor() // = 11
What you want is somewhat unusual, but it is possible.
public struct ScreenInfo {
private static var _scaleFactor: Int?
public static var scaleFactor: Int? {
set {
if _scaleFactor == nil {
_scaleFactor = newValue
} else {
// Optionally throw an exception or something
}
}
get {
return _scaleFactor
}
}
}
ScreenInfo.scaleFactor // nil
ScreenInfo.scaleFactor = 5
ScreenInfo.scaleFactor // 5
ScreenInfo.scaleFactor = 15
ScreenInfo.scaleFactor // 5

Xamarin UITableView RowSelection

I have a simple UITableView and I am loading the data by implementing UITableViewSource. I also have a label on the same page. I would like to display the selected row from tableview on label.
This is my MainViewController:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
List<string> lstTableSource = new List<string> ();
for (int intCounter=0; intCounter<5; intCounter++)
{
lstTableSource.Add ("Row "+intCounter);
}
tblTest.Source = new TableSource (lstTableSource.ToArray());
}
public void Test(string strSelected)
{
lblTest.Text = strSelected;
}
This is my row selection method in TableSource.cs
public override void RowSelected (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
MainViewControllerObj.Test (tableItems[indexPath.Row]);
}
When i tap on the row system is throwing NullReferenceException on
lblTest.Text = strSelected;
Program works fine if I print the value of the variable strSelected on a Console.