我正在尝试为评论和回复面板创建一个两级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();
}
}
以下是该问题的一些屏幕截图:
答案 0 :(得分:1)
您不需要使用两个表格视图,您可以尝试使用一个表格,将顶级注释作为部分,所有回复都作为该部分的单元格。
答案 1 :(得分:1)
正如Pradeep所说,一个带有单元格的桌面视图是正确的选择。但是说过会有很多条件。因此,您的数据结构应该能够处理它。