mirror of
https://github.com/zhigang1992/GitHawk.git
synced 2026-04-29 12:35:00 +08:00
refactor collapse cells (#1406)
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
import UIKit
|
||||
import IGListKit
|
||||
|
||||
final class IssueCommentCodeBlockCell: IssueCommentBaseCell, ListBindable, CollapsibleCell {
|
||||
final class IssueCommentCodeBlockCell: IssueCommentBaseCell, ListBindable {
|
||||
|
||||
static let scrollViewInset = UIEdgeInsets(
|
||||
top: Styles.Sizes.rowSpacing,
|
||||
@@ -26,7 +26,6 @@ final class IssueCommentCodeBlockCell: IssueCommentBaseCell, ListBindable, Colla
|
||||
|
||||
let textView = AttributedStringView()
|
||||
let scrollView = UIScrollView()
|
||||
let overlay = CreateCollapsibleOverlay()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@@ -58,7 +57,6 @@ final class IssueCommentCodeBlockCell: IssueCommentBaseCell, ListBindable, Colla
|
||||
width: contentView.bounds.width - inset.left - inset.right,
|
||||
height: scrollView.contentSize.height
|
||||
)
|
||||
LayoutCollapsible(layer: overlay, view: contentView)
|
||||
}
|
||||
|
||||
// MARK: ListBindable
|
||||
@@ -72,10 +70,4 @@ final class IssueCommentCodeBlockCell: IssueCommentBaseCell, ListBindable, Colla
|
||||
textView.configureAndSizeToFit(text: viewModel.code, width: 0)
|
||||
}
|
||||
|
||||
// MARK: CollapsibleCell
|
||||
|
||||
func setCollapse(visible: Bool) {
|
||||
overlay.isHidden = !visible
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// CollapsibleCell.swift
|
||||
// Freetime
|
||||
//
|
||||
// Created by Ryan Nystrom on 5/28/17.
|
||||
// Copyright © 2017 Ryan Nystrom. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
let CollapseCellMinHeight: CGFloat = 20
|
||||
|
||||
func CreateCollapsibleOverlay() -> CALayer {
|
||||
let layer = CAGradientLayer()
|
||||
layer.isHidden = true
|
||||
layer.colors = [
|
||||
UIColor(white: 1, alpha: 0).cgColor,
|
||||
UIColor(white: 0, alpha: 0.1).cgColor
|
||||
]
|
||||
return layer
|
||||
}
|
||||
|
||||
func LayoutCollapsible(layer: CALayer, view: UIView) {
|
||||
if layer.superlayer != view.layer {
|
||||
view.layer.addSublayer(layer)
|
||||
}
|
||||
|
||||
let bounds = view.bounds
|
||||
|
||||
// disable implicit CALayer animations
|
||||
CATransaction.begin()
|
||||
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
|
||||
layer.frame = CGRect(
|
||||
x: 0,
|
||||
y: bounds.height - CollapseCellMinHeight,
|
||||
width: bounds.width,
|
||||
height: CollapseCellMinHeight
|
||||
)
|
||||
CATransaction.commit()
|
||||
}
|
||||
|
||||
protocol CollapsibleCell {
|
||||
|
||||
func setCollapse(visible: Bool)
|
||||
|
||||
}
|
||||
@@ -19,9 +19,7 @@ protocol IssueCommentImageHeightCellDelegate: class {
|
||||
func imageDidFinishLoad(cell: IssueCommentImageCell, url: URL, size: CGSize)
|
||||
}
|
||||
|
||||
final class IssueCommentImageCell: IssueCommentBaseCell,
|
||||
ListBindable,
|
||||
CollapsibleCell {
|
||||
final class IssueCommentImageCell: IssueCommentBaseCell, ListBindable {
|
||||
|
||||
weak var delegate: IssueCommentImageCellDelegate?
|
||||
weak var heightDelegate: IssueCommentImageHeightCellDelegate?
|
||||
@@ -29,7 +27,6 @@ CollapsibleCell {
|
||||
let imageView = FLAnimatedImageView()
|
||||
|
||||
private let spinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
|
||||
private let overlay = CreateCollapsibleOverlay()
|
||||
private var tapGesture: UITapGestureRecognizer!
|
||||
|
||||
override init(frame: CGRect) {
|
||||
@@ -59,7 +56,6 @@ CollapsibleCell {
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
LayoutCollapsible(layer: overlay, view: contentView)
|
||||
|
||||
var frame = bounds
|
||||
if let size = imageView.image?.size {
|
||||
@@ -105,12 +101,6 @@ CollapsibleCell {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: CollapsibleCell
|
||||
|
||||
func setCollapse(visible: Bool) {
|
||||
overlay.isHidden = !visible
|
||||
}
|
||||
|
||||
// MARK: UIGestureRecognizerDelegate
|
||||
|
||||
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
@@ -120,7 +110,7 @@ CollapsibleCell {
|
||||
// only start the image tap gesture when an image exists
|
||||
// and the tap is within the actual image's bounds
|
||||
guard let image = imageView.image,
|
||||
overlay.isHidden
|
||||
collapsed == false
|
||||
else { return false }
|
||||
|
||||
let imageSize = image.size
|
||||
|
||||
@@ -20,7 +20,7 @@ private func bodyIsCollapsible(body: Any) -> Bool {
|
||||
func IssueCollapsedBodies(bodies: [AnyObject], width: CGFloat) -> (AnyObject, CGFloat)? {
|
||||
let cap: CGFloat = 300
|
||||
// minimum height to collapse so expanding shows significant amount of content
|
||||
let minDelta = CollapseCellMinHeight * 3
|
||||
let minDelta = IssueCommentBaseCell.collapseCellMinHeight * 3
|
||||
|
||||
var totalHeight: CGFloat = 0
|
||||
for body in bodies {
|
||||
@@ -34,7 +34,7 @@ func IssueCollapsedBodies(bodies: [AnyObject], width: CGFloat) -> (AnyObject, CG
|
||||
if bodyIsCollapsible(body: body),
|
||||
totalHeight > cap,
|
||||
totalHeight - cap > minDelta {
|
||||
let collapsedBodyHeight = max(cap - (totalHeight - height), CollapseCellMinHeight)
|
||||
let collapsedBodyHeight = max(cap - (totalHeight - height), IssueCommentBaseCell.collapseCellMinHeight)
|
||||
return (body, collapsedBodyHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ protocol IssueCommentDoubleTapDelegate: class {
|
||||
|
||||
class IssueCommentBaseCell: UICollectionViewCell, UIGestureRecognizerDelegate {
|
||||
|
||||
static let collapseCellMinHeight: CGFloat = 20
|
||||
|
||||
enum BorderType {
|
||||
case head
|
||||
case neck
|
||||
@@ -28,6 +30,8 @@ class IssueCommentBaseCell: UICollectionViewCell, UIGestureRecognizerDelegate {
|
||||
|
||||
private let borderLayer = CAShapeLayer()
|
||||
private let backgroundLayer = CAShapeLayer()
|
||||
private let collapseLayer = CAGradientLayer()
|
||||
private let collapseButton = UIButton()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@@ -50,6 +54,26 @@ class IssueCommentBaseCell: UICollectionViewCell, UIGestureRecognizerDelegate {
|
||||
backgroundLayer.strokeColor = nil
|
||||
backgroundLayer.fillColor = UIColor.white.cgColor
|
||||
layer.insertSublayer(backgroundLayer, at: 0)
|
||||
|
||||
collapseLayer.isHidden = true
|
||||
collapseLayer.colors = [
|
||||
UIColor(white: 1, alpha: 0).cgColor,
|
||||
UIColor(white: 1, alpha: 1).cgColor
|
||||
]
|
||||
|
||||
collapseButton.setImage(UIImage(named: "bullets")?.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||
collapseButton.backgroundColor = Styles.Colors.Blue.medium.color
|
||||
collapseButton.accessibilityTraits = UIAccessibilityTraitNone
|
||||
collapseButton.tintColor = .white
|
||||
collapseButton.titleLabel?.font = Styles.Fonts.smallTitle
|
||||
collapseButton.clipsToBounds = true
|
||||
collapseButton.isHidden = true
|
||||
collapseButton.contentEdgeInsets = UIEdgeInsets(top: -2, left: 8, bottom: -2, right: 8)
|
||||
collapseButton.imageEdgeInsets = .zero
|
||||
collapseButton.sizeToFit()
|
||||
collapseButton.layer.cornerRadius = collapseButton.bounds.height / 2
|
||||
collapseButton.isUserInteractionEnabled = false // allow tap to pass through to cell
|
||||
contentView.addSubview(collapseButton)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
@@ -114,6 +138,29 @@ class IssueCommentBaseCell: UICollectionViewCell, UIGestureRecognizerDelegate {
|
||||
|
||||
borderLayer.frame = self.bounds
|
||||
backgroundLayer.frame = self.bounds
|
||||
|
||||
if collapseLayer.isHidden == false {
|
||||
contentView.layer.addSublayer(collapseLayer)
|
||||
contentView.bringSubview(toFront: collapseButton)
|
||||
|
||||
let collapseFrame = CGRect(
|
||||
x: bounds.minX,
|
||||
y: bounds.height - IssueCommentBaseCell.collapseCellMinHeight,
|
||||
width: bounds.width,
|
||||
height: IssueCommentBaseCell.collapseCellMinHeight
|
||||
)
|
||||
|
||||
// disable implicit CALayer animations
|
||||
CATransaction.begin()
|
||||
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
|
||||
collapseLayer.frame = collapseFrame
|
||||
CATransaction.commit()
|
||||
|
||||
collapseButton.center = CGPoint(
|
||||
x: collapseFrame.width / 2,
|
||||
y: collapseFrame.minY + collapseButton.bounds.height / 2 - 3
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override var backgroundColor: UIColor? {
|
||||
@@ -130,6 +177,14 @@ class IssueCommentBaseCell: UICollectionViewCell, UIGestureRecognizerDelegate {
|
||||
doubleTapDelegate?.didDoubleTap(cell: self)
|
||||
}
|
||||
|
||||
var collapsed: Bool = false {
|
||||
didSet {
|
||||
collapseButton.isHidden = !collapsed
|
||||
collapseLayer.isHidden = !collapsed
|
||||
setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UIGestureRecognizerDelegate
|
||||
|
||||
func gestureRecognizer(
|
||||
|
||||
@@ -123,8 +123,8 @@ IssueCommentDoubleTapDelegate {
|
||||
private func clearCollapseCells() {
|
||||
// clear any collapse state before updating so we don't have a dangling overlay
|
||||
for cell in collectionContext?.visibleCells(for: self) ?? [] {
|
||||
if let cell = cell as? CollapsibleCell {
|
||||
cell.setCollapse(visible: false)
|
||||
if let cell = cell as? IssueCommentBaseCell {
|
||||
cell.collapsed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,8 +284,8 @@ IssueCommentDoubleTapDelegate {
|
||||
else { fatalError("Cell not bindable") }
|
||||
|
||||
// extra config outside of bind API. applies to multiple cell types.
|
||||
if let cell = cell as? CollapsibleCell {
|
||||
cell.setCollapse(visible: collapsed && (viewModel as AnyObject) === object?.collapse?.model)
|
||||
if let cell = cell as? IssueCommentBaseCell {
|
||||
cell.collapsed = collapsed && (viewModel as AnyObject) === object?.collapse?.model
|
||||
}
|
||||
|
||||
// connect specific cell delegates
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import UIKit
|
||||
import IGListKit
|
||||
|
||||
final class IssueCommentQuoteCell: IssueCommentBaseCell, ListBindable, CollapsibleCell {
|
||||
final class IssueCommentQuoteCell: IssueCommentBaseCell, ListBindable {
|
||||
|
||||
static let borderWidth: CGFloat = 2
|
||||
static func inset(quoteLevel: Int) -> UIEdgeInsets {
|
||||
@@ -23,7 +23,6 @@ final class IssueCommentQuoteCell: IssueCommentBaseCell, ListBindable, Collapsib
|
||||
|
||||
let textView = AttributedStringView()
|
||||
private var borders = [UIView]()
|
||||
private let overlay = CreateCollapsibleOverlay()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@@ -37,7 +36,6 @@ final class IssueCommentQuoteCell: IssueCommentBaseCell, ListBindable, Collapsib
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
LayoutCollapsible(layer: overlay, view: contentView)
|
||||
textView.reposition(width: contentView.bounds.width)
|
||||
for (i, border) in borders.enumerated() {
|
||||
border.frame = CGRect(
|
||||
@@ -71,10 +69,4 @@ final class IssueCommentQuoteCell: IssueCommentBaseCell, ListBindable, Collapsib
|
||||
setNeedsLayout()
|
||||
}
|
||||
|
||||
// MARK: CollapsibleCell
|
||||
|
||||
func setCollapse(visible: Bool) {
|
||||
overlay.isHidden = !visible
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,10 +10,9 @@ import UIKit
|
||||
import SnapKit
|
||||
import IGListKit
|
||||
|
||||
final class IssueCommentSummaryCell: IssueCommentBaseCell, ListBindable, CollapsibleCell {
|
||||
final class IssueCommentSummaryCell: IssueCommentBaseCell, ListBindable {
|
||||
|
||||
let label = UILabel()
|
||||
let overlay = CreateCollapsibleOverlay()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@@ -31,11 +30,6 @@ final class IssueCommentSummaryCell: IssueCommentBaseCell, ListBindable, Collaps
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
LayoutCollapsible(layer: overlay, view: contentView)
|
||||
}
|
||||
|
||||
// MARK: ListBindable
|
||||
|
||||
func bindViewModel(_ viewModel: Any) {
|
||||
@@ -43,10 +37,4 @@ final class IssueCommentSummaryCell: IssueCommentBaseCell, ListBindable, Collaps
|
||||
label.text = "▶ \(viewModel.summary)"
|
||||
}
|
||||
|
||||
// MARK: CollapsibleCell
|
||||
|
||||
func setCollapse(visible: Bool) {
|
||||
overlay.isHidden = !visible
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import UIKit
|
||||
import IGListKit
|
||||
|
||||
final class IssueCommentTextCell: IssueCommentBaseCell, ListBindable, CollapsibleCell {
|
||||
final class IssueCommentTextCell: IssueCommentBaseCell, ListBindable {
|
||||
|
||||
static let inset = UIEdgeInsets(
|
||||
top: 0,
|
||||
@@ -19,7 +19,6 @@ final class IssueCommentTextCell: IssueCommentBaseCell, ListBindable, Collapsibl
|
||||
)
|
||||
|
||||
let textView = AttributedStringView()
|
||||
let overlay = CreateCollapsibleOverlay()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
@@ -35,7 +34,6 @@ final class IssueCommentTextCell: IssueCommentBaseCell, ListBindable, Collapsibl
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
LayoutCollapsible(layer: overlay, view: contentView)
|
||||
textView.reposition(width: contentView.bounds.width)
|
||||
}
|
||||
|
||||
@@ -55,10 +53,4 @@ final class IssueCommentTextCell: IssueCommentBaseCell, ListBindable, Collapsibl
|
||||
textView.configureAndSizeToFit(text: viewModel, width: contentView.bounds.width)
|
||||
}
|
||||
|
||||
// MARK: CollapsibleCell
|
||||
|
||||
func setCollapse(visible: Bool) {
|
||||
overlay.isHidden = !visible
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
292FA33F1FBCE4D000BBB0BB /* GithubClient+Milestones.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292FA33E1FBCE4D000BBB0BB /* GithubClient+Milestones.swift */; };
|
||||
292FCAF61EDFCC510026635E /* IssueCommentCodeBlockCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292FCACA1EDFCC510026635E /* IssueCommentCodeBlockCell.swift */; };
|
||||
292FCAF71EDFCC510026635E /* IssueCommentCodeBlockModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292FCACB1EDFCC510026635E /* IssueCommentCodeBlockModel.swift */; };
|
||||
292FCAF81EDFCC510026635E /* CollapsibleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292FCACC1EDFCC510026635E /* CollapsibleCell.swift */; };
|
||||
292FCAF91EDFCC510026635E /* IssueCommentDetailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292FCACE1EDFCC510026635E /* IssueCommentDetailCell.swift */; };
|
||||
292FCAFA1EDFCC510026635E /* IssueCommentDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292FCACF1EDFCC510026635E /* IssueCommentDetailsViewModel.swift */; };
|
||||
292FCAFB1EDFCC510026635E /* IssueCommentImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292FCAD11EDFCC510026635E /* IssueCommentImageCell.swift */; };
|
||||
@@ -504,7 +503,6 @@
|
||||
292FA33E1FBCE4D000BBB0BB /* GithubClient+Milestones.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GithubClient+Milestones.swift"; sourceTree = "<group>"; };
|
||||
292FCACA1EDFCC510026635E /* IssueCommentCodeBlockCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueCommentCodeBlockCell.swift; sourceTree = "<group>"; };
|
||||
292FCACB1EDFCC510026635E /* IssueCommentCodeBlockModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueCommentCodeBlockModel.swift; sourceTree = "<group>"; };
|
||||
292FCACC1EDFCC510026635E /* CollapsibleCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollapsibleCell.swift; sourceTree = "<group>"; };
|
||||
292FCACE1EDFCC510026635E /* IssueCommentDetailCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueCommentDetailCell.swift; sourceTree = "<group>"; };
|
||||
292FCACF1EDFCC510026635E /* IssueCommentDetailsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueCommentDetailsViewModel.swift; sourceTree = "<group>"; };
|
||||
292FCAD11EDFCC510026635E /* IssueCommentImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueCommentImageCell.swift; sourceTree = "<group>"; };
|
||||
@@ -1021,7 +1019,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
292FCAC91EDFCC510026635E /* CodeBlock */,
|
||||
292FCACC1EDFCC510026635E /* CollapsibleCell.swift */,
|
||||
292FCACD1EDFCC510026635E /* Details */,
|
||||
2949674A1EF9714900B1CF1A /* Hr */,
|
||||
2949674F1EFC1E9200B1CF1A /* Html */,
|
||||
@@ -2270,7 +2267,6 @@
|
||||
29CEA5CD1F84DB1B009827DB /* BaseListViewController.swift in Sources */,
|
||||
98835BD41F1A17EE005BA24F /* Bundle+Version.swift in Sources */,
|
||||
29316DB51ECC7DEB007CAE3F /* ButtonCell.swift in Sources */,
|
||||
292FCAF81EDFCC510026635E /* CollapsibleCell.swift in Sources */,
|
||||
295F52AD1EF1BE83000B53CF /* CommentModelsFromMarkdown.swift in Sources */,
|
||||
D8BAD0661FDF224600C41071 /* WrappingStaticSpacingFlowLayout.swift in Sources */,
|
||||
2919295A1F3FB1D20012067B /* Content.swift in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user