// // MasterTimelineTableViewCell.swift // NetNewsWire // // Created by Brent Simmons on 8/31/15. // Copyright © 2015 Ranchero Software, LLC. All rights reserved. // import UIKit import RSCore class MasterTimelineTableViewCell: UITableViewCell { private let titleView = MasterTimelineTableViewCell.multiLineUILabel() private let summaryView = MasterTimelineTableViewCell.singleLineUILabel() private let textView = MasterTimelineTableViewCell.multiLineUILabel() private let unreadIndicatorView = MasterUnreadIndicatorView(frame: CGRect.zero) private let dateView = MasterTimelineTableViewCell.singleLineUILabel() private let feedNameView = MasterTimelineTableViewCell.singleLineUILabel() private lazy var avatarImageView: UIImageView = { let imageView = NonIntrinsicImageView(image: AppAssets.feedImage) imageView.contentMode = .scaleAspectFit return imageView }() private lazy var starView = { return UIImageView(image: AppAssets.timelineStarImage) }() private lazy var textFields = { return [self.dateView, self.feedNameView, self.titleView, self.summaryView, self.textView] }() var cellData: MasterTimelineCellData! { didSet { updateSubviews() } } required init?(coder: NSCoder) { super.init(coder: coder) commonInit() } override var frame: CGRect { didSet { setNeedsLayout() } } override func layoutSubviews() { super.layoutSubviews() let layoutRects = updatedLayoutRects() setFrame(for: titleView, rect: layoutRects.titleRect) setFrame(for: summaryView, rect: layoutRects.summaryRect) setFrame(for: textView, rect: layoutRects.textRect) dateView.setFrameIfNotEqual(layoutRects.dateRect) unreadIndicatorView.setFrameIfNotEqual(layoutRects.unreadIndicatorRect) feedNameView.setFrameIfNotEqual(layoutRects.feedNameRect) avatarImageView.setFrameIfNotEqual(layoutRects.avatarImageRect) starView.setFrameIfNotEqual(layoutRects.starRect) } } // MARK: - Private private extension MasterTimelineTableViewCell { static func singleLineUILabel() -> UILabel { let label = NonIntrinsicLabel() label.lineBreakMode = .byTruncatingTail label.allowsDefaultTighteningForTruncation = false return label } static func multiLineUILabel() -> UILabel { let label = NonIntrinsicLabel() label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping label.allowsDefaultTighteningForTruncation = false return label } func setFrame(for label: UILabel, rect: CGRect) { if Int(floor(rect.height)) == 0 || Int(floor(rect.width)) == 0 { hideView(label) } else { showView(label) label.setFrameIfNotEqual(rect) } } func addSubviewAtInit(_ view: UIView, hidden: Bool) { addSubview(view) view.translatesAutoresizingMaskIntoConstraints = false view.isHidden = hidden } func commonInit() { theme() addAccessoryView() addSubviewAtInit(titleView, hidden: false) addSubviewAtInit(summaryView, hidden: true) addSubviewAtInit(textView, hidden: true) addSubviewAtInit(unreadIndicatorView, hidden: true) addSubviewAtInit(dateView, hidden: false) addSubviewAtInit(feedNameView, hidden: true) addSubviewAtInit(avatarImageView, hidden: true) addSubviewAtInit(starView, hidden: true) } func theme() { let bgView = UIView() bgView.backgroundColor = AppAssets.selectionBackgroundColor selectedBackgroundView = bgView } func addAccessoryView() { accessoryView = UIImageView(image: AppAssets.chevronRightImage) } func updatedLayoutRects() -> MasterTimelineCellLayout { return MasterTimelineCellLayout(width: bounds.width, height: bounds.height, cellData: cellData, hasAvatar: avatarImageView.image != nil) } func updateTitleView() { titleView.font = MasterTimelineCellLayout.titleFont titleView.textColor = MasterTimelineCellLayout.titleColor updateTextFieldText(titleView, cellData?.title) } func updateSummaryView() { summaryView.font = MasterTimelineCellLayout.textFont summaryView.textColor = MasterTimelineCellLayout.textColor updateTextFieldText(summaryView, cellData?.text) } func updateTextView() { textView.font = MasterTimelineCellLayout.textFont textView.textColor = MasterTimelineCellLayout.textColor updateTextFieldText(textView, cellData?.text) } func updateDateView() { dateView.font = MasterTimelineCellLayout.dateFont dateView.textColor = MasterTimelineCellLayout.dateColor updateTextFieldText(dateView, cellData.dateString) } func updateTextFieldText(_ label: UILabel, _ text: String?) { let s = text ?? "" if label.text != s { label.text = s setNeedsLayout() } } func updateFeedNameView() { if cellData.showFeedName { showView(feedNameView) feedNameView.font = MasterTimelineCellLayout.feedNameFont feedNameView.textColor = MasterTimelineCellLayout.feedColor updateTextFieldText(feedNameView, cellData.feedName) } else { hideView(feedNameView) } } func updateUnreadIndicator() { showOrHideView(unreadIndicatorView, cellData.read || cellData.starred) } func updateStarView() { showOrHideView(starView, !cellData.starred) } func updateAvatar() { // The avatar should be bigger than a favicon. They’re too small; they look weird. let minDimension = 22.0 * RSScreen.mainScreenScale guard let image = cellData.avatar, cellData.showAvatar, image.size.height >= minDimension, image.size.width >= minDimension else { makeAvatarEmpty() return } showView(avatarImageView) avatarImageView.layer.cornerRadius = MasterTimelineCellLayout.avatarCornerRadius avatarImageView.clipsToBounds = true if avatarImageView.image !== image { avatarImageView.image = image setNeedsLayout() } } func makeAvatarEmpty() { if avatarImageView.image != nil { avatarImageView.image = nil setNeedsLayout() } hideView(avatarImageView) } func hideView(_ view: UIView) { if !view.isHidden { view.isHidden = true } } func showView(_ view: UIView) { if view.isHidden { view.isHidden = false } } func showOrHideView(_ view: UIView, _ shouldHide: Bool) { shouldHide ? hideView(view) : showView(view) } func updateSubviews() { updateTitleView() updateSummaryView() updateTextView() updateDateView() updateFeedNameView() updateUnreadIndicator() updateStarView() updateAvatar() } }