// // Copyright (c) 2019 Open Whisper Systems. All rights reserved. // public protocol CaptionContainerViewDelegate: class { func captionContainerViewDidUpdateText(_ captionContainerView: CaptionContainerView) } public class CaptionContainerView: UIView { weak var delegate: CaptionContainerViewDelegate? var currentText: String? { get { return currentCaptionView.text } set { currentCaptionView.text = newValue delegate?.captionContainerViewDidUpdateText(self) } } var pendingText: String? { get { return pendingCaptionView.text } set { pendingCaptionView.text = newValue delegate?.captionContainerViewDidUpdateText(self) } } func updatePagerTransition(ratioComplete: CGFloat) { if let currentText = self.currentText, currentText.count > 0 { currentCaptionView.alpha = 1 - ratioComplete } else { currentCaptionView.alpha = 0 } if let pendingText = self.pendingText, pendingText.count > 0 { pendingCaptionView.alpha = ratioComplete } else { pendingCaptionView.alpha = 0 } } func completePagerTransition() { updatePagerTransition(ratioComplete: 1) // promote "pending" to "current" caption view. let oldCaptionView = self.currentCaptionView self.currentCaptionView = self.pendingCaptionView self.pendingCaptionView = oldCaptionView self.pendingText = nil self.currentCaptionView.alpha = 1 self.pendingCaptionView.alpha = 0 } // MARK: Initializers override init(frame: CGRect) { super.init(frame: frame) setContentHuggingHigh() setCompressionResistanceHigh() addSubview(currentCaptionView) currentCaptionView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top) currentCaptionView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual) pendingCaptionView.alpha = 0 addSubview(pendingCaptionView) pendingCaptionView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top) pendingCaptionView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual) } public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: Subviews private var pendingCaptionView: CaptionView = CaptionView() private var currentCaptionView: CaptionView = CaptionView() } private class CaptionView: UIView { var text: String? { get { return textView.text } set { if let captionText = newValue, captionText.count > 0 { textView.text = captionText } else { textView.text = nil } } } // MARK: Subviews let textView: CaptionTextView = { let textView = CaptionTextView() textView.font = UIFont.ows_dynamicTypeBody textView.textColor = Colors.text textView.backgroundColor = .clear textView.isEditable = false textView.isSelectable = false return textView }() let scrollFadeView = GradientView(from: .clear, to: .black) // MARK: Initializers override init(frame: CGRect) { super.init(frame: frame) addSubview(textView) textView.autoPinEdgesToSuperviewMargins() addSubview(scrollFadeView) scrollFadeView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top) scrollFadeView.autoSetDimension(.height, toSize: 20) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: UIView overrides override func layoutSubviews() { super.layoutSubviews() scrollFadeView.isHidden = !textView.doesContentNeedScroll } // MARK: - class CaptionTextView: UITextView { var kMaxHeight: CGFloat = ScaleFromIPhone5(200) override var text: String! { didSet { invalidateIntrinsicContentSize() } } override var font: UIFont? { didSet { invalidateIntrinsicContentSize() } } var doesContentNeedScroll: Bool { return self.bounds.height == kMaxHeight } override func layoutSubviews() { super.layoutSubviews() // Enable/disable scrolling depending on wether we've clipped // content in `intrinsicContentSize` isScrollEnabled = doesContentNeedScroll } override var intrinsicContentSize: CGSize { var size = super.intrinsicContentSize if size.height == UIView.noIntrinsicMetric { size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom } size.height = min(kMaxHeight, size.height) return size } } }