Open timahrentlov opened 12 years ago
Can you paste a screenshot of what is it that you see and what it is that you want?
Sure,
I get something like this:
when what I really want is something like this:
It would be really nice if the existing Date/Time Element's could be set up to use the InputView model instead.
Although not finished and with issues (got to stop working for today), I wipped up something along these lines:
[Preserve(AllMembers=true)] public class DatePickerElement : Element { static NSString key = new NSString ("datepickerelement"); public DateTime dateTime; DatePickerCell _cell;
public class DatePickerCell : UITableViewCell
{
DateTime _dateTime;
UITableViewCell cellView;
UIDatePicker _dp = null;
UIView _dpview = null;
UITextField _dummyFld;
UITextField _entryFld;
public DatePickerCell (DateTime newdate, NSString identKey) : base (UITableViewCellStyle.Default, identKey)
{
// Configure your cell here: selection style, colors, properties
_dateTime = newdate;
// Create Date Picker
_dp = new UIDatePicker(RectangleF.Empty);
_dp.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
_dp.Date = newdate;
_dp.Mode = UIDatePickerMode.Date;
_dp.ValueChanged += delegate { OnValueChanged();};
_dpview = new UIView(_dp.Bounds);
_dpview.AddSubview(_dp);
// Add Controls
_dummyFld = new UITextField(Bounds);
_dummyFld.Tag = 1;
_dummyFld.Text = newdate.ToString();
_dummyFld.BackgroundColor = UIColor.Clear;
_dummyFld.ShouldBeginEditing += delegate
{
_entryFld.BecomeFirstResponder();
return false;
};
_entryFld = new UITextField(Bounds);
_entryFld.Hidden = true;
_entryFld.Tag = 2;
_entryFld.InputView = _dpview;
_entryFld.BackgroundColor = UIColor.Clear;
_entryFld.Started += delegate
{
};
_entryFld.Ended += delegate
{
_dateTime = _dp.Date;
};
// Update Fld views
_dummyFld.AddSubview(_entryFld);
cellView = new UITableViewCell();
cellView.AddSubview(_dummyFld);
ContentView.AddSubview(cellView);
}
public override void LayoutSubviews ()
{
base.LayoutSubviews ();
cellView.Frame = ContentView.Bounds;
cellView.SetNeedsDisplay ();
}
// Called by our client code when we get new data.
public void UpdateCell(DateTime newdate)
{
_dateTime = newdate;
}
public void OnValueChanged()
{
_entryFld.ResignFirstResponder();
}
}
public DatePickerElement (DateTime newDate) : base (null)
{
dateTime = newDate;
}
public override UITableViewCell GetCell (UITableView tv)
{
var cell = tv.DequeueReusableCell (key) as DatePickerCell;
if (cell == null)
cell = new DatePickerCell(dateTime, key);
else
cell.UpdateCell(dateTime);
_cell = cell;
return cell;
}
public override void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath path)
{
base.Selected (dvc, tableView, path);
_cell.BecomeFirstResponder();
}
// Own Delegate
public void BeforeRemove()
{
}
}
As a general observation I believe that pickers of various sort should be prioritized. Stuff like #of: hours, months weeks, etc are sorely needed when it comes to making entry forms in a hurry. Again, with InputView as the dominant way of showing pickers, since I can always use a sub-root to implement sub-level functionality. I need these things like, yesterday. At least if productivity is a virtue :)
I am not sure what the user experience should be like if the user touches the on/off switch. I do not know how to detect that the user is messing around with another cell to hide the other cells.
Apple sort of works around that issue by limiting the content that you can click on in the Settings app. Do you have links to other apps that mix date entry with other entries that I could take a look at?
Also, do you care to explain why you added those UITextFields to your sample? I am not sure what it is that you were doing there.
Well, one could always resort to the crude old:
Perhaps one could do a BecomeFirstResponder on newly tapped cells instead? Thus removing the inputview? Worst case, could you not remember the last element tapped and resign it when a new one is tapped? In this more elegant input model the update of the , say, date "text" inside the element should happen instantly while turning the picker wheel.
One thing is certain though. The current implementation does not work so even the crude model would be preferable to status quo.
As mentioned, I had a look at https://gist.github.com/965257 and copied much of its code-idea. I too wondered why 2 fields on top of each other was necessary in the example. But after playing with the code I could see that you needed a hidden field on top of an editable field in order to avoid manual text input. It looks like fields need to be editable and actually be able to receive input to produce an inputview, but I have not researched using inputview on other controls than UILabel (where inputview seems readonly) and UITextField.
For the "crude" model I would use something like the InputAccessoryView.
Or perhaps somehing like this https://github.com/sickanimations/ActionSheetPicker where actionsheets and buttons are used. After some research it looks like that modal presentation (which often is the case for forms that creates entries) and inputviews are hard to combine.
After investigating many approaches, some of which gets your app rejected by Apple, I think I'm going to roll with something like this on iPad:
public class MyDatePicker
{
UIViewController _vc;
UIPopoverController _pop;
DateTime _dateTime;
UIDatePicker _dp = null;
public MyDatePicker (Element element)
{
_vc = new UIViewController();
_pop = new UIPopoverController(_vc);
_pop.DidDismiss += delegate { OnDone();};
_vc.View.Frame = new RectangleF(0,0,340,300);
// Create Date Picker
_dp = new UIDatePicker(new RectangleF(0,0,320,300));
_dp.Date = DateTime.Now;
_dp.Mode = UIDatePickerMode.Date;
_dp.ValueChanged += delegate { OnValueChanged();};
// Add to view
_vc.View.AddSubview(_dp);
// Present
var activecell = element.GetActiveCell();
_pop.SetPopoverContentSize(new SizeF(_dp.Bounds.Width,_dp.Bounds.Height), true);
_pop.PresentFromRect(activecell.Frame,activecell.Superview,UIPopoverArrowDirection.Left,true);
}
public void OnDone()
{
}
public void OnValueChanged()
{}
}
To all that currently have a need for a quick ipad alternative, here are some classes that I use until Miquel incorporates something fancier :)
public class DatePickerElement : StringElement
{
DatePickerHelper _currdatepickerhelper;
DateTime _currdate;
OnPickerEvent _ope;
[Preserve(AllMembers=true)]
public class DatePickerHelper
{
UIViewController _vc;
UIPopoverController _pop;
UIDatePicker _dp = null;
DatePickerElement _element;
public DatePickerHelper (DatePickerElement element)
{
_vc = new UIViewController();
_pop = new UIPopoverController(_vc);
_pop.DidDismiss += delegate { OnDone();};
_vc.View.Frame = new RectangleF(0,0,340,300);
// Create Date Picker
_dp = new UIDatePicker(new RectangleF(0,0,320,300));
_dp.Date = element.GetDate ();
_dp.Mode = UIDatePickerMode.Date;
_dp.ValueChanged += delegate { OnValueChanged();};
// Add to view
_vc.View.AddSubview(_dp);
// Present
var activecell = element.GetActiveCell();
_pop.SetPopoverContentSize(new SizeF(_dp.Bounds.Width,_dp.Bounds.Height), true);
_pop.PresentFromRect(activecell.Frame,activecell.Superview,UIPopoverArrowDirection.Down,true);
// Remember element
_element = element;
}
public void OnDone()
{
_element.ResetPicker();
}
public void OnValueChanged()
{
_element.SetDate(_dp.Date);
}
}
public DatePickerElement (string initcaption, DateTime initdate, OnPickerEvent onpickerevent) : base ("")
{
Caption = initcaption;
SetDate (initdate);
_ope = onpickerevent;
}
public override void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath indexPath)
{
base.Selected (dvc, tableView, indexPath);
_currdatepickerhelper = new DatePickerHelper(this);
}
public void ResetPicker()
{
_currdatepickerhelper = null;
if (_ope != null)
_ope();
}
public void SetDate(DateTime newdate)
{
_currdate = newdate.ToLocalTime();
Value = _currdate.ToLongDateString();
if (GetContainerTableView() != null)
GetContainerTableView().ReloadData();
}
public DateTime GetDate()
{
return _currdate;
}
}
public class TimePickerElement : StringElement
{
TimePickerHelper _currtimepickerhelper;
DateTime _currtime;
OnPickerEvent _ope;
[Preserve(AllMembers=true)]
public class TimePickerHelper
{
UIViewController _vc;
UIPopoverController _pop;
UIDatePicker _dp = null;
TimePickerElement _element;
public TimePickerHelper (TimePickerElement element)
{
_vc = new UIViewController();
_pop = new UIPopoverController(_vc);
_pop.DidDismiss += delegate { OnDone();};
_vc.View.Frame = new RectangleF(0,0,150,300);
// Create Date Picker
_dp = new UIDatePicker(new RectangleF(0,0,150,300));
_dp.Date = element.GetTime ();
_dp.Mode = UIDatePickerMode.Time;
_dp.ValueChanged += delegate { OnValueChanged();};
// Add to view
_vc.View.AddSubview(_dp);
// Present
var activecell = element.GetActiveCell();
_pop.SetPopoverContentSize(new SizeF(_dp.Bounds.Width,_dp.Bounds.Height), true);
_pop.PresentFromRect(activecell.Frame,activecell.Superview,UIPopoverArrowDirection.Down,true);
// Remember element
_element = element;
}
public void OnDone()
{
_element.ResetPicker();
}
public void OnValueChanged()
{
_element.SetTime(_dp.Date);
}
}
public TimePickerElement (string initcaption, DateTime inittime, OnPickerEvent onpickerevent) : base ("")
{
Caption = initcaption;
SetTime (inittime);
_ope = onpickerevent;
}
public override void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath indexPath)
{
base.Selected (dvc, tableView, indexPath);
_currtimepickerhelper = new TimePickerHelper(this);
}
public void ResetPicker()
{
_currtimepickerhelper = null;
if (_ope != null)
_ope();
}
public void SetTime(DateTime newtime)
{
_currtime = newtime.ToLocalTime();;
Value = _currtime.ToShortTimeString();
if (GetContainerTableView() != null)
GetContainerTableView().ReloadData();
}
public DateTime GetTime()
{
return _currtime;
}
}
public class DurationPickerElement : StringElement
{
DurationPickerHelper _currdurationpickerhelper;
double _currduration;
OnPickerEvent _ope;
[Preserve(AllMembers=true)]
public class DurationPickerHelper
{
UIViewController _vc;
UIPopoverController _pop;
UIDatePicker _dp = null;
DurationPickerElement _element;
public DurationPickerHelper (DurationPickerElement element)
{
_vc = new UIViewController();
_pop = new UIPopoverController(_vc);
_pop.DidDismiss += delegate { OnDone();};
_vc.View.Frame = new RectangleF(0,0,340,300);
// Create Date Picker
_dp = new UIDatePicker(new RectangleF(0,0,320,300));
_dp.Mode = UIDatePickerMode.CountDownTimer;
_dp.MinuteInterval = 5;
_dp.CountDownDuration = element.GetDuration();
_dp.ValueChanged += delegate { OnValueChanged();};
// Add to view
_vc.View.AddSubview(_dp);
// Present
var activecell = element.GetActiveCell();
_pop.SetPopoverContentSize(new SizeF(_dp.Bounds.Width,_dp.Bounds.Height), true);
_pop.PresentFromRect(activecell.Frame,activecell.Superview,UIPopoverArrowDirection.Down,true);
// Remember element
_element = element;
}
public void OnDone()
{
_element.ResetPicker();
}
public void OnValueChanged()
{
_element.SetDuration(_dp.CountDownDuration);
}
}
public DurationPickerElement (string initcaption, double initduration, OnPickerEvent onpickerevent) : base ("")
{
Caption = initcaption;
SetDuration (initduration);
_ope = onpickerevent;
}
public override void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath indexPath)
{
base.Selected (dvc, tableView, indexPath);
_currdurationpickerhelper = new DurationPickerHelper(this);
}
public void ResetPicker()
{
_currdurationpickerhelper = null;
if (_ope != null)
_ope();
}
public void SetDuration(double newduration)
{
_currduration = newduration;
if (newduration != 0.0)
{
var hour = (int)_currduration / 3600;
var min = (int)(_currduration % 3600)/60;
Value = String.Format("{0:00}:{1:00}",hour, min);
} else
Value = "";
if (GetContainerTableView() != null)
GetContainerTableView().ReloadData();
}
public double GetDuration()
{
return _currduration;
}
}
null - will your above code work for the iPhone as well? If so, can you please point me to the definition for OnPickerEvent. I can't seem to find it anywhere. Thanks!
public delegate void OnPickerEvent(); The code is iPad only.
I've submit a pull request to get started on this: https://github.com/migueldeicaza/MonoTouch.Dialog/pull/107
And here is it with date pickers: https://github.com/johnsimons/MonoTouch.Dialog/tree/AllPickers
Nice job, John. Hopefully it gets included soon.
I've been experimenting with creating data-entry form using dialogs. Many aspects are covered by the current version of Dialogs. And many thanks to Miquel for making our lives easier. But stuff like DatePickers on ipads does not seem that useful right now.
When choosing a DataTimeElement, it pushes an entire new view instead of using the InputView trick like this one seems to do: https://gist.github.com/965257