migueldeicaza / MonoTouch.Dialog

Tools to simplify creating dialogs with the user using MonoTouch
MIT License
430 stars 211 forks source link

EntryElement Caption overlaps the TextField #230

Open therealjohn opened 9 years ago

therealjohn commented 9 years ago

When the Caption of an EntryElement is long, instead of truncating, it overlaps the text field.

stesvis commented 9 years ago

Same here, please see attachments.

ios simulator screen shot dec 23 2014 10 22 23 am ios simulator screen shot dec 23 2014 10 22 56 am

danmiser commented 8 years ago

Would love to see this fixed as well. Part of the problem is that the GetCell method creates a UITableViewCell and also creates and modifies the UITextField. All of the UITextField related things should be happening in that cell instead of the EntryElement.GetCell method. Alternatively, allowing for specifying of a custom cell class to use for creation would be a great thing not only for the EntryElement, but all of the elements.

therealjohn commented 8 years ago

@danmiser Im curious what would be a good fix for this. If the label was truncated, it still is a strange thing to present to a user. The real issue is the text is too long. Replacing an overlap with a truncation doesnt really help present the data better. What would work better?

AlexW68 commented 8 years ago

This was a real issue for me, I solved it by adding a parameter to entry element called xPos it tells the control where to start the entry element, totally solved my problem.

nfloat sizeWidth = size.Width; nfloat sizeHeight = size.Height; // I pass xPos which is the position the textfield will start so I can correctly position every entry. // for this I use figures like 150 if (xPos > -1) { // this deals with getting the width right so the clear button is displayed correctly. sizeWidth = xPos; if (UIApplication.SharedApplication.StatusBarOrientation == UIInterfaceOrientation.Portrait || UIApplication.SharedApplication.StatusBarOrientation == UIInterfaceOrientation.PortraitUpsideDown) { width = (UIScreen.MainScreen.Bounds.Width - sizeWidth); } else { width = (UIScreen.MainScreen.Bounds.Height - sizeWidth); } if (WizPR.CoreIOS.Instance.IsIOS7OrBetter == false) { //width = 200; width -= UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone ? 30 : 180; } else { // actually could not get this to work any other way width = 200; //width -= UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone ? 30 : 400; } } var entryFrame = new CoreGraphics.CGRect(sizeWidth, yOffset, width, sizeHeight);

This works really well for me, I added this to all the appropriate elements.

hope it helps.

danmiser commented 8 years ago

I think the real solution is to have a custom UITableViewCell for each element. Here's one implementation: https://github.com/xamarin/monotouch-samples/blob/master/MonoDevelopTouchCells/CustomCell.cs

I also found this as a good starting point, but the code is so old that it would have to have some extensive rework to be sure that all of the core MTD code is in there as well: https://github.com/escoz/MonoMobile.Forms/blob/master/MonoTouch.Dialog/Views/EntryElementCell.cs

By doing this, we separate what is in the cell from the element. It would be great to be able to extend things in our code but it's more of a copy/paste from the code right now due to the high cohesion of the code.

I agree with you about the size of the labels being the real problem. But the entry element is just unusable as it is right now with long captions. At least truncation would allow a clean experience. The reason it is an issue for me is the captions are coming from user defined values. If it was just me making the labels, I'd shorten them, but I don't have control over that.

billhatfield99 commented 8 years ago

This is still a problem with the current build of MTDialog. I've resolved it by inheriting from EntryElement to create a new FixedEntryElement class that overrides the GetCell() method. It gives the label a third of the width and the text entry two thirds. It works consistently from initial display and across refreshes. In addition to adding this class to your project, you'll also need to change the EntryElement class source (in Elements.cs) to make these variables accessible and overridable from your descendant class.


        // Accessibility/overridability changed to allow access from descendents
        protected UITextField entry;
        protected static UIFont font = UIFont.SystemFontOfSize(17);
        protected UIReturnKeyType? returnKeyType = null;
        protected bool becomeResponder;
        public virtual event Func<bool> ShouldReturn;

I moved these all from their original locations in the EntryElement class to one spot so that I'll know what changes need to be made again if I update Elements.cs in the future.

Once you've made the variables available, create and use this class instead of EntryElement. Note that GetCell() here is so long because it is essentially re-implementing exactly what's done in EntryElement GetCell(). The primary difference is near the top of GetCell() where the size and width variables are defined.

using System;
using CoreGraphics;
using System;
using CoreGraphics;
using UIKit;

namespace MonoTouch.Dialog
{
    // FixedEntryElement addresses the label/text overlap seen in EntryElement when 
    // when returning to the page after selecting from a list, etc. It re-implements
    // the GetCell method - the code is largely the same, but instead of dynamically
    // trying to figure out where things go (which is where the issue is), this 
    // implementation gives the label 1/3 of the width and the entry 2/3rds. This
    // is hard-coded now, but could be made adjustable.

    public class FixedEntryElement : EntryElement
    {
        public override event Func<bool> ShouldReturn;

        public FixedEntryElement(string caption, string placeholder = "", string initialValue = "")
            : base(caption, placeholder, initialValue) { }

        UITableViewCell cell;
        public override UITableViewCell GetCell(UITableView tv)
        {
            if (cell == null)
            {
                cell = new UITableViewCell(UITableViewCellStyle.Default, CellKey);
                cell.SelectionStyle = UITableViewCellSelectionStyle.None;
                cell.TextLabel.Font = font;

            }
            cell.TextLabel.Text = Caption;

            var offset = (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone) ? 20 : 90;
            cell.Frame = new CGRect(cell.Frame.X, cell.Frame.Y, tv.Frame.Width - offset, this.cell.Frame.Height);
            CGSize size = new CGSize((cell.ContentView.Bounds.Width / 3), 35);
            nfloat yOffset = (cell.ContentView.Bounds.Height - size.Height) / 2 - 1;
            nfloat width = (cell.ContentView.Bounds.Width - size.Width - 30);

            var entryFrame = new CGRect(size.Width, yOffset, width, size.Height);

            if (entry == null)
            {
                entry = CreateTextField(entryFrame);
                entry.EditingChanged += delegate
                {
                    if (NotifyChangedOnKeyStroke)
                    {
                        FetchValue();
                    }
                };
                entry.ValueChanged += delegate
                {
                    FetchValue();
                };
                entry.Ended += delegate
                {
                    FetchValue();
                    if (EntryEnded != null)
                    {
                        EntryEnded(this, null);
                    }
                };
                entry.ShouldReturn += delegate
                {

                    if (ShouldReturn != null)
                        return ShouldReturn();

                    RootElement root = GetImmediateRootElement();
                    FixedEntryElement focus = null;

                    if (root == null)
                        return true;

                    foreach (var s in root.Sections)
                    {
                        foreach (var e in s.Elements)
                        {
                            if (e == this)
                            {
                                focus = this;
                            }
                            else if (focus != null && e is EntryElement)
                            {
                                focus = e as FixedEntryElement;
                                break;
                            }
                        }

                        if (focus != null && focus != this )
                            break;
                    }

                    if (focus != this)
                        focus.BecomeFirstResponder(true);
                    else
                        focus.ResignFirstResponder(true);

                    return true;
                };
                entry.Started += delegate
                {
                    FixedEntryElement self = null;

                    if (EntryStarted != null)
                    {
                        EntryStarted(this, null);
                    }

                    if (!returnKeyType.HasValue)
                    {
                        var returnType = UIReturnKeyType.Default;

                        foreach (var e in (Parent as Section).Elements)
                        {
                            if (e == this)
                                self = this;
                            else if (self != null && e is EntryElement)
                                returnType = UIReturnKeyType.Next;
                        }
                        entry.ReturnKeyType = returnType;
                    }
                    else
                        entry.ReturnKeyType = returnKeyType.Value;

                    tv.ScrollToRow(IndexPath, UITableViewScrollPosition.Middle, true);
                };
                this.cell.ContentView.AddSubview(entry);
            }
            else
                entry.Frame = entryFrame;

            if (becomeResponder)
            {
                entry.BecomeFirstResponder();
                becomeResponder = false;
            }
            entry.KeyboardType = KeyboardType;
            entry.EnablesReturnKeyAutomatically = EnablesReturnKeyAutomatically;
            entry.AutocapitalizationType = AutocapitalizationType;
            entry.AutocorrectionType = AutocorrectionType;

            return cell;
        }
    }
}