c# - tableview xamarin ios
UITableViewCell personalizado: la primera fila no tiene contenido (2)
Como dijo Pradeep, una vista de tabla con celdas sería la opción correcta. Pero habiendo dicho que habrá muchas condiciones. Por lo tanto, su estructura de datos debe poder manejarlo.
- Cree una matriz de diccionarios con una enumeración de tipo de celda (respuesta o comentario) y rellene los valores en consecuencia.
- Puedes mostrar / ocultar el separador dentro del método GetCell.
- Incluso puedes usar sangría si necesitas en el futuro mostrar la respuesta como lo hace Facebook.
Estoy tratando de crear una implementación de UITableView de dos niveles para un panel de comentarios y respuestas. El primer nivel contiene todos los comentarios de nivel superior y si hay respuestas a ese comentario, habrá un indicador. Cuando toca en una celda de comentarios de nivel superior, el panel animará un nuevo UITableView a la vista. La primera celda es el comentario que el usuario pulsó y debajo hay una celda para cada respuesta a ese comentario.
Lo logro utilizando dos UITableViews diferentes y dos UITableViewSources diferentes (pero comparten la misma clase base). Cuando el usuario toca un comentario de nivel superior, el controlador que administra las tablas (CommentPanelViewController) anima la vista anterior (comentarios de nivel superior) y la vista nueva (respuestas) a la vista.
La cuestión:
Cuando toco el comentario de nivel superior, no aparece nada más que su indicador. Todas las demás respuestas se muestran bien, pero el comentario de nivel superior no tiene texto, ningún autor y ninguna marca de tiempo.
Para mantener las cosas concisas y fáciles de seguir, publicaré solo el código necesario. La vista de comentarios de nivel superior funciona perfectamente, solo la vista de respuesta tiene errores, por lo que comenzaremos allí:
CommentSource - la fuente de la tabla base
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 - la fuente para comentarios de nivel superior
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 - fuente para las respuestas
public class ReplyViewSource : CommentSource
{
protected override int CommentLineCount => 0;
protected override string CellId => "ReplyCell";
}
Entonces, cuando se selecciona un comentario de nivel superior, se llama a CommentViewSource.RowSelected que llama a CommentViewSource.CommentSelected que se maneja:
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;
..........
}
Y ahora para el grande, el UITableViewCell personalizado. Esta clase se usa tanto para las respuestas como para los comentarios de nivel superior:
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();
}
}
Aquí hay algunas capturas de pantalla del problema:
no necesita usar dos vistas de tabla, puede probar con una tabla haciendo comentarios de nivel superior como sección y todas las respuestas como celdas para esa sección.