Подтвердить что ты не робот

Пользовательский UITableViewCell: у первой строки нет содержимого

Я пытаюсь создать двухуровневую реализацию UITableView для панели комментариев и ответов. Первый уровень содержит все комментарии верхнего уровня, и если есть ответы на этот комментарий, будет индикатор. Когда вы нажимаете на ячейку комментариев верхнего уровня, панель будет анимировать новый UITableView в представлении. Первая ячейка - это комментарий, который пользователь прослушивает и находится ниже, который является ячейкой для каждого ответа на этот комментарий.

Выполняю, используя два разных UITableViews и два разных UITableViewSources (но они имеют один и тот же базовый класс). Когда пользователь нажимает на комментарий верхнего уровня, контроллер, который управляет таблицами (CommentPanelViewController), анимирует старый вид (комментарии верхнего уровня) с глаз долой и новый вид (ответы) в поле зрения.

Проблема:

Когда я нажимаю на комментарий верхнего уровня, появляется только индикатор. Все остальные ответы отображаются отлично, но комментарий верхнего уровня не имеет текста, нет автора и не имеет отметки времени.

Чтобы держать вещи краткими и легко следовать, я вывешу только нужный код. Представление комментариев на верхнем уровне отлично работает, только представление ответа ошибочно, поэтому мы начнем там:

CommentSource - источник базовой таблицы

public abstract class CommentSource : UITableViewSource
{
    protected List<Comment> _data;

    public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
    {
        var comment = _data[indexPath.Row];
        var cell = tableView.DequeueReusableCell(CellId)
            as CommentCell ?? new CommentCell(new NSString("CellId"), CommentLineCount,
            comment.Replies != null && comment.Replies.Count > 0);

        cell.SelectionStyle = UITableViewCellSelectionStyle.None;
        cell.LayoutMargins = UIEdgeInsets.Zero;
        cell.SeparatorInset = UIEdgeInsets.Zero;
        cell.SetNeedsUpdateConstraints();
        cell.UpdateConstraintsIfNeeded();
        cell.UpdateCell(comment);
        cell.DrawIndicator(comment);

        DrawAccessories(comment, cell);

        return cell;
    }

    protected virtual void DrawAccessories(Comment comment, CommentCell cell) { }

    protected abstract int CommentLineCount { get; }

    protected abstract string CellId { get; }

    public override nint RowsInSection(UITableView tableview, nint section) => _data?.Count ?? 0;

    public void UpdateData(IEnumerable<Comment> comments)
    {
        _data = OrderComments(comments);
    }

    private static List<Comment> OrderComments(IEnumerable<Comment> comments) =>
        comments?.OrderBy(x => x.CreatedDateTime).ToList();
}

CommentViewSource - источник комментариев верхнего уровня

public class CommentViewSource : CommentSource
{
    protected override int CommentLineCount => 3;
    protected override string CellId => "CommentCell";

    public Action<Comment, bool> CommentSelected { get; set; }

    public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
    {
        var commentCell = tableView.CellAt(indexPath) as CommentCell;
        CommentSelected(_data[indexPath.Row], commentCell != null && commentCell.IsEllipsed);
    }

    protected override void DrawAccessories(Comment comment, CommentCell cell)
    {
        base.DrawAccessories(comment, cell);
        if (comment.Replies.Count > 0)
        {
            cell.DrawReplyCountIndicator(comment);
        }
    }
}

ReplyViewSource - источник ответов

public class ReplyViewSource : CommentSource
{
    protected override int CommentLineCount => 0;

    protected override string CellId => "ReplyCell";
}

Итак, когда выбран комментарий верхнего уровня, вызывается CommentViewSource.RowSelected, который вызывает обработчик CommentViewSource.CommentSelected:

CommentPanelViewController.Constructor:

public CommentPanelViewController(CommentViewSource commentSource,
    CommentSource replySource, Action dismissHandler)
{
    _isReplyVisible = false;
    _commentSource = commentSource;
    _commentSource.CommentSelected += (comment, isEllipsed) =>
    {
        if (comment.Replies.Count <= 0 && !isEllipsed) { return; }

        var replies = new List<Comment>(comment.Replies);
        if (!replies.Contains(comment))
        {
            replies.Insert(0, comment);
        }
        _replySource.UpdateData(replies);
        _replyView.Table.ReloadData();
        AnimateReplyView(true);
    };
    _replySource = replySource;

    ..........
 }

И теперь для большого, пользовательского UITableViewCell. Этот класс используется как для ответов, так и для комментариев верхнего уровня:

CommentCell

public sealed class CommentCell : UITableViewCell
{
    private const string CustomCommentCss =
        "<style>*{{font-family:{0};font-size:{1};color:{2};}}span{{font-weight:600;}}</style>";
    private readonly bool _hasReplies;
    private readonly UILabel _creatorLabel;
    private readonly UILabel _commentLabel;
    private readonly UILabel _dateLabel;
    private readonly UIFont _font;
    private bool _didUpdateConstraints;
    private UIView _indicator;
    private ReplyCountIndicatorView _replyCountIndicator;

    public CommentCell(NSString cellId, int numberOfLines, bool hasReplies) :
        base(UITableViewCellStyle.Default, cellId)
    {
        _hasReplies = hasReplies;
        _didUpdateConstraints = false;
        SelectionStyle = UITableViewCellSelectionStyle.None;

        var textColor = Globals.ColorDark;

        _font = UIFont.FromName(Globals.FontSanFranLight, Globals.FontSizeBody);
        _creatorLabel = new UILabel
        {
            Font = UIFont.FromName(Globals.FontSanFranSemiBold, Globals.FontSizeBody),
            Lines = 1,
            LineBreakMode = UILineBreakMode.TailTruncation,
            TextColor = textColor
        };
        _commentLabel = new UILabel
        {
            Font = _font,
            Lines = numberOfLines,
            LineBreakMode = UILineBreakMode.TailTruncation,
            TextColor = textColor
        };
        _dateLabel = new UILabel
        {
            Font = UIFont.FromName(Globals.FontSanFranLight, Globals.FontSizeSmall),
            TextColor = Globals.ColorDisabled
        };

        ContentView.AddSubviews(_creatorLabel, _commentLabel, _dateLabel);
    }

    public bool IsEllipsed => _commentLabel.Text.StringSize(
        _commentLabel.Font).Width > 3 * _commentLabel.Bounds.Size.Width;

    public override void UpdateConstraints()
    {
        base.UpdateConstraints();
        _creatorLabel.SetContentCompressionResistancePriority(1000, UILayoutConstraintAxis.Vertical);
        _commentLabel.SetContentCompressionResistancePriority(1000, UILayoutConstraintAxis.Vertical);
        _dateLabel.SetContentCompressionResistancePriority(1000, UILayoutConstraintAxis.Vertical);
        _replyCountIndicator?.SetContentCompressionResistancePriority(1000, UILayoutConstraintAxis.Vertical);

        if (_didUpdateConstraints || (_replyCountIndicator == null && _hasReplies)) { return; }

        var leftMargin = AnnotationIndicator.Size.Width + 2 * Globals.MarginGrid;

        if (_replyCountIndicator != null && _hasReplies)
        {
            ContentView.ConstrainLayout(() =>
                _creatorLabel.Frame.Top == ContentView.Frame.Top + Globals.MarginGrid &&
                _creatorLabel.Frame.Left == ContentView.Frame.Left + leftMargin &&
                _creatorLabel.Frame.Right == ContentView.Frame.Right - Globals.MarginGrid &&

                _commentLabel.Frame.Top == _creatorLabel.Frame.Bottom + Globals.MarginGrid / 4 &&
                _commentLabel.Frame.Left == _creatorLabel.Frame.Left &&
                _commentLabel.Frame.Right == _creatorLabel.Frame.Right &&

                _dateLabel.Frame.Top == _commentLabel.Frame.Bottom + Globals.MarginGrid / 4 &&
                _dateLabel.Frame.Left == _creatorLabel.Frame.Left &&
                _dateLabel.Frame.Right == _creatorLabel.Frame.Right &&

                _replyCountIndicator.Frame.Top == _dateLabel.Frame.Bottom + Globals.MarginGrid &&
                _replyCountIndicator.Frame.Left == _dateLabel.Frame.Left &&
                _replyCountIndicator.Frame.Width == Globals.SmallToolbarItemSize &&
                _replyCountIndicator.Frame.Height == Globals.SmallToolbarItemSize &&
                _replyCountIndicator.Frame.Bottom == ContentView.Frame.Bottom - Globals.MarginGrid);   
        }
        else
        {
            ContentView.ConstrainLayout(() =>
                _creatorLabel.Frame.Top == ContentView.Frame.Top + Globals.MarginGrid &&
                _creatorLabel.Frame.Left == ContentView.Frame.Left + leftMargin &&
                _creatorLabel.Frame.Right == ContentView.Frame.Right - Globals.MarginGrid &&

                _commentLabel.Frame.Top == _creatorLabel.Frame.Bottom + Globals.MarginGrid / 4 &&
                _commentLabel.Frame.Left == _creatorLabel.Frame.Left &&
                _commentLabel.Frame.Right == _creatorLabel.Frame.Right &&

                _dateLabel.Frame.Top == _commentLabel.Frame.Bottom + Globals.MarginGrid / 4 &&
                _dateLabel.Frame.Left == _creatorLabel.Frame.Left &&
                _dateLabel.Frame.Right == _creatorLabel.Frame.Right &&
                _dateLabel.Frame.Bottom == ContentView.Frame.Bottom - Globals.MarginGrid);
        }

        _didUpdateConstraints = true;
    }

    public void UpdateCell(Comment comment)
    {
        // update the comment author
        _creatorLabel.Text = string.IsNullOrWhiteSpace(comment.CreatedByUser.FirstName) &&
            string.IsNullOrWhiteSpace(comment.CreatedByUser.LastName) ?
                comment.CreatedByUser.Email :
                $"{comment.CreatedByUser.FirstName} {comment.CreatedByUser.LastName}";

        // update the text
        var attr = new NSAttributedStringDocumentAttributes { DocumentType = NSDocumentType.HTML,  };
        var nsError = new NSError();

        var text = comment.Text.Insert(0, string.Format(CustomCommentCss,
            _font.FontDescriptor.Name, _font.PointSize,
            ColorConverter.ConvertToHex(_commentLabel.TextColor)));
        var mutableString = new NSMutableAttributedString(new NSAttributedString(
            text, attr, ref nsError));
        var mutableParagraph = new NSMutableParagraphStyle
        {
            Alignment = UITextAlignment.Left,
            LineBreakMode = UILineBreakMode.TailTruncation
        };
        mutableString.AddAttribute(UIStringAttributeKey.ParagraphStyle, mutableParagraph,
            new NSRange(0, mutableString.Length));
        mutableString.AddAttribute(UIStringAttributeKey.StrokeColor, Globals.ColorDark,
            new NSRange(0, mutableString.Length));
        _commentLabel.AttributedText = mutableString;

        // update the timestamp
        var localTime = TimeZone.CurrentTimeZone.ToLocalTime(
            comment.LastModifiedDateTime).ToString("g");
        _dateLabel.Text = comment.LastModifiedDateTime == comment.CreatedDateTime ? 
            localTime : $"Modified {localTime}";
    }

    public void DrawIndicator(Comment comment)
    {
        // if we've already drawn the indicator and 
        // the comment has no annotation associated with it
        _indicator?.RemoveFromSuperview();

        // if the comment havs an annotation associated with it,
        // draw the annotation indicator
        if (comment.Annotation != null)
        {
            _indicator = new AnnotationIndicator
            {
                Location = new CGPoint(Globals.MarginGrid, Globals.MarginGrid),
                Number = comment.Annotation.AnnotationNumber,
                FillColor = Color.FromHex(comment.Annotation.FillColorValue).ToUIColor(),
                TextColor = Color.FromHex(comment.Annotation.TextColorValue).ToUIColor()
            };
            AddSubview(_indicator);
        }

        // otherwise, draw the general comment indicator
        else
        {
            var size = comment.IsReply ? ReplyIndicator.DotSize : AnnotationIndicator.Size;
            _indicator = comment.IsReply ?
                new ReplyIndicator
                {
                    Frame = new CGRect(Globals.MarginGrid + size.Width / 2,
                        Globals.MarginGrid + size.Height / 2, ReplyIndicator.DotSize.Width,
                        ReplyIndicator.DotSize.Height)
                } as UIView :
                new UIImageView
                {
                    Image = UIImage.FromFile("general_annotation_indicator.png"),
                    Frame = new CGRect(Globals.MarginGrid, Globals.MarginGrid, size.Width, size.Height)
                };
            AddSubview(_indicator);
        }
    }

    public void DrawReplyCountIndicator(Comment comment)
    {
        if (_replyCountIndicator != null) { return; }

        _replyCountIndicator = new ReplyCountIndicatorView(comment.Replies.Count);
        ContentView.AddSubview(_replyCountIndicator);

        _didUpdateConstraints = false;
        UpdateConstraints();
    }
}

Вот несколько скриншотов проблемы:

Работа с комментариями верхнего уровня

Ответы, не так много

4b9b3361

Ответ 1

вам не нужно использовать два вида таблицы, которые вы можете попробовать с одной таблицей, сделав комментарий верхнего уровня как раздел и все ответы как ячейки для этого раздела.

Ответ 2

Как сказал Прадип, один вариант таблицы с ячейками будет правильным вариантом. Но, сказав, что будет много условий. Таким образом, ваша структура данных должна иметь возможность обрабатывать ее.

  • Создать массив словарей с перечислением типа ячейки (ответ или комментарий) и соответствующим образом заполнить значения.
  • Вы можете показать/скрыть разделитель внутри метода GetCell.
  • Даже вы можете использовать отступы, если вам нужно в будущем, чтобы продемонстрировать ответ, как это делает Facebook.