Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 87 additions & 50 deletions BeeSwift/Cells/GoalCollectionViewCell.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// GoalTableViewCell.swift
// GoalCollectionViewCell.swift
// BeeSwift
//
// Created by Andy Brett on 4/24/15.
Expand All @@ -10,73 +10,110 @@ import BeeKit
import Foundation

class GoalCollectionViewCell: UICollectionViewCell {
let slugLabel: BSLabel = BSLabel()
let titleLabel: BSLabel = BSLabel()
let todaytaLabel: BSLabel = BSLabel()
let thumbnailImageView = GoalImageView(isThumbnail: true)
let safesumLabel: BSLabel = BSLabel()
let margin = 8
private lazy var cardView: CardView = {
let view = CardView()
view.backgroundColor = .secondarySystemGroupedBackground
view.layer.cornerRadius = CardLookConstants.cornerRadius
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.1
view.layer.shadowRadius = 4
view.layer.shadowOffset = CardLookConstants.shadowOffset
return view
}()

private lazy var thumbnailImageView: GoalImageView = { return GoalImageView(isThumbnail: true) }()

private lazy var titleLabel: BSLabel = {
let label = BSLabel()
label.font = UIFont.beeminder.defaultFontHeavy.withSize(17)
label.textColor = .label
return label
}()

private lazy var slugLabel: BSLabel = {
let label = BSLabel()
label.font = UIFont.beeminder.defaultFont.withSize(15)
label.textColor = .label
return label
}()

private lazy var countdownLabel: BSLabel = {
let label = BSLabel()
label.font = UIFont.beeminder.defaultBoldFont.withSize(13)
label.numberOfLines = 0
return label
}()

private lazy var todaytaLabel: BSLabel = {
let label = BSLabel()
label.textColor = .label
label.font = UIFont.beeminder.defaultFont.withSize(14)
label.textAlignment = .right
label.setContentCompressionResistancePriority(.required, for: .horizontal)
return label
}()

override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.addSubview(self.slugLabel)
self.contentView.addSubview(self.titleLabel)
self.contentView.addSubview(self.todaytaLabel)
self.contentView.addSubview(self.thumbnailImageView)
self.contentView.addSubview(self.safesumLabel)
self.contentView.backgroundColor = .systemBackground

self.slugLabel.font = UIFont.beeminder.defaultFontHeavy
self.slugLabel.textColor = .label
self.slugLabel.snp.makeConstraints { (make) -> Void in
make.left.equalTo(self.margin)
make.top.equalTo(10)
make.width.lessThanOrEqualTo(self.contentView).multipliedBy(0.35)
setUpView()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
}

private func setUpView() {
self.contentView.backgroundColor = .clear
self.contentView.addSubview(self.cardView)

self.cardView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(6) }

[self.thumbnailImageView, self.titleLabel, self.todaytaLabel, self.slugLabel, self.countdownLabel].forEach {
self.cardView.addSubview($0)
}
self.titleLabel.font = UIFont.beeminder.defaultFont
self.titleLabel.textColor = .label
self.titleLabel.textAlignment = .left
self.titleLabel.snp.makeConstraints { (make) -> Void in
make.centerY.equalTo(self.slugLabel)
make.left.equalTo(self.slugLabel.snp.right).offset(10)
make.right.lessThanOrEqualTo(self.todaytaLabel.snp.left).offset(-10)

self.thumbnailImageView.snp.makeConstraints { make in
make.left.top.bottom.equalToSuperview().inset(CardLookConstants.spacing)
make.width.equalTo(CGFloat(Constants.thumbnailWidth))
make.height.equalTo(CGFloat(Constants.thumbnailHeight))
}
self.todaytaLabel.font = UIFont.beeminder.defaultFont
self.todaytaLabel.textColor = .label
self.todaytaLabel.textAlignment = .right
self.todaytaLabel.snp.makeConstraints { (make) -> Void in
make.centerY.equalTo(self.slugLabel)
make.right.equalTo(-self.margin)

self.titleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(CardLookConstants.verticalPadding)
make.left.equalTo(self.thumbnailImageView.snp.right).offset(CardLookConstants.spacing)
make.right.equalTo(self.todaytaLabel.snp.left).offset(-8)
}
self.todaytaLabel.setContentCompressionResistancePriority(.required, for: .horizontal)

self.thumbnailImageView.snp.makeConstraints { (make) -> Void in
make.left.equalTo(0).offset(self.margin)
make.top.equalTo(self.slugLabel.snp.bottom).offset(5)
make.height.equalTo(Constants.thumbnailHeight)
make.width.equalTo(Constants.thumbnailWidth)
self.todaytaLabel.snp.makeConstraints { make in
make.centerY.equalTo(self.titleLabel)
make.right.equalToSuperview().offset(-CardLookConstants.horizontalPadding)
}

self.safesumLabel.textAlignment = NSTextAlignment.center
self.safesumLabel.font = UIFont.beeminder.defaultBoldFont.withSize(13)
self.safesumLabel.numberOfLines = 0
self.safesumLabel.snp.makeConstraints { (make) -> Void in
make.left.equalTo(self.thumbnailImageView.snp.right).offset(5)
make.centerY.equalTo(self.thumbnailImageView.snp.centerY)
make.right.equalTo(-self.margin)
self.slugLabel.snp.makeConstraints { make in
make.top.equalTo(self.titleLabel.snp.bottom).offset(2)
make.left.right.equalTo(self.titleLabel)
}

self.countdownLabel.snp.makeConstraints { make in
make.top.equalTo(self.slugLabel.snp.bottom).offset(4)
make.left.right.equalTo(self.titleLabel)
make.bottom.lessThanOrEqualToSuperview().offset(-CardLookConstants.verticalPadding)
}
}
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }

override func prepareForReuse() {
super.prepareForReuse()
configure(with: nil)
self.configure(with: nil)
}

func configure(with goal: Goal?) {
self.thumbnailImageView.goal = goal
self.titleLabel.text = goal?.title
self.slugLabel.text = goal?.slug
self.titleLabel.isHidden = goal?.title == goal?.slug
self.todaytaLabel.text = goal?.todayta == true ? "✓" : ""
self.safesumLabel.text = goal?.capitalSafesum()
self.safesumLabel.textColor = goal?.countdownColor ?? UIColor.Beeminder.gray
self.countdownLabel.text = goal?.capitalSafesum()
self.countdownLabel.textColor = goal?.countdownColor ?? UIColor.Beeminder.SafetyBuffer.gray
}
}
36 changes: 22 additions & 14 deletions BeeSwift/Components/GoalImageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ class GoalImageView: UIView {

private func setupView() {
self.addSubview(imageView)
imageView.snp.makeConstraints { (make) in make.edges.equalToSuperview() }

self.layer.cornerRadius = CardLookConstants.cornerRadius
self.layer.borderWidth = 0
self.clipsToBounds = true
imageView.snp.makeConstraints { $0.edges.equalToSuperview() }

self.imageView.image = UIImage(named: "GraphPlaceholder")

self.addSubview(beeLemniscateView)
beeLemniscateView.snp.makeConstraints { (make) in make.edges.equalToSuperview() }
beeLemniscateView.snp.makeConstraints { $0.edges.equalToSuperview() }
beeLemniscateView.isHidden = true

NotificationCenter.default.addObserver(
Expand All @@ -60,6 +65,7 @@ class GoalImageView: UIView {
imageView.image = UIImage(named: "GraphPlaceholder")
currentlyShowingGraph = false
beeLemniscateView.isHidden = true
layer.borderWidth = 0
}

@MainActor private func showGraphImage(image: UIImage) {
Expand All @@ -73,18 +79,20 @@ class GoalImageView: UIView {
duration: duration,
options: .transitionCrossDissolve,
animations: { [weak self] in
self?.imageView.image = image
self?.beeLemniscateView.isHidden = self?.goal == nil || self?.goal?.queued == false

if self?.isThumbnail == true {
self?.imageView.layer.borderColor = self?.goal?.countdownColor.cgColor
self?.imageView.layer.borderWidth = self?.goal == nil ? 0 : 1
} else {
self?.imageView.layer.borderColor = nil
self?.imageView.layer.borderWidth = 0
guard let self else { return }
self.imageView.image = image
self.beeLemniscateView.isHidden = self.goal == nil || self.goal?.queued == false
self.imageView.contentMode = self.isThumbnail == true ? .scaleAspectFill : .scaleAspectFit
},
completion: { [weak self] _ in
guard let self else { return }
if self.isThumbnail {
self.layer.borderColor = self.goal?.countdownColor.cgColor
self.layer.borderWidth = self.goal == nil ? 0 : 2
}
self.currentlyShowingGraph = true
}
) { [weak self] _ in self?.currentlyShowingGraph = true }
)
}

@MainActor private func refresh() {
Expand All @@ -104,8 +112,8 @@ class GoalImageView: UIView {
return
}

// - Deadbeat: Placeholder, no animation
if goal.owner.deadbeat {
// Deadbeat: Placeholder, no animation
guard !goal.owner.deadbeat else {
clearGoalGraph()
return
}
Expand Down
15 changes: 15 additions & 0 deletions BeeSwift/Components/UI/CardLookConstants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Part of BeeSwift. Copyright Beeminder

struct CardLookConstants {
static let cornerRadius: CGFloat = 12
static let shadowOpacity: Float = 0.08
static let shadowRadius: CGFloat = 6
static let shadowOffset = CGSize(width: 0, height: 2)
static let horizontalPadding: CGFloat = 16
static let verticalPadding: CGFloat = 14
static let spacing: CGFloat = 12
static let primaryBackground = UIColor.secondarySystemBackground
static let secondaryBackground = UIColor.tertiarySystemBackground
static let borderColor = UIColor.separator
static let borderWidth: CGFloat = 0.5
}
40 changes: 40 additions & 0 deletions BeeSwift/Components/UI/CardView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Part of BeeSwift. Copyright Beeminder

import UIKit

class CardView: UIView {
enum Style {
case primary
case secondary
case tertiary
}
var style: Style = .primary { didSet { updateStyle() } }
var cornerRadius: CGFloat = CardLookConstants.cornerRadius { didSet { layer.cornerRadius = cornerRadius } }
var shadowOpacity: Float = CardLookConstants.shadowOpacity { didSet { layer.shadowOpacity = shadowOpacity } }
var shadowRadius: CGFloat = CardLookConstants.shadowRadius { didSet { layer.shadowRadius = shadowRadius } }
var shadowOffset: CGSize = CardLookConstants.shadowOffset { didSet { layer.shadowOffset = shadowOffset } }
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
backgroundColor = CardLookConstants.primaryBackground
layer.cornerRadius = cornerRadius
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = shadowOpacity
layer.shadowRadius = shadowRadius
layer.shadowOffset = shadowOffset
layer.masksToBounds = false
}
private func updateStyle() {
switch style {
case .primary: backgroundColor = CardLookConstants.primaryBackground
case .secondary: backgroundColor = CardLookConstants.secondaryBackground
case .tertiary: backgroundColor = .clear
}
}
}
8 changes: 4 additions & 4 deletions BeeSwift/Gallery/GalleryViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class GalleryViewController: UIViewController {
}()
private lazy var collectionContainer = UIView()
private lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: stackView.frame, collectionViewLayout: self.collectionViewLayout)
collectionView.backgroundColor = .systemBackground
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: self.collectionViewLayout)
collectionView.backgroundColor = .systemGray6
collectionView.alwaysBounceVertical = true
collectionView.register(
UICollectionReusableView.self,
Expand Down Expand Up @@ -144,7 +144,7 @@ class GalleryViewController: UIViewController {
object: nil
)
self.view.addSubview(self.stackView)
stackView.snp.makeConstraints { (make) -> Void in make.edges.equalToSuperview() }
self.stackView.snp.makeConstraints { (make) -> Void in make.edges.equalToSuperview() }

NotificationCenter.default.addObserver(
self,
Expand Down Expand Up @@ -473,7 +473,7 @@ extension GalleryViewController: UICollectionViewDelegateFlowLayout {
let minimumWidth: CGFloat = 320
let itemSpacing = self.collectionViewLayout.minimumInteritemSpacing
let availableWidth =
collectionView.frame.width - collectionView.contentInset.left - collectionView.contentInset.right
collectionView.bounds.width - collectionView.contentInset.left - collectionView.contentInset.right
// Calculate how many cells could fit at the minimum width, rounding down (as we can't show a fractional cell)
// We need to account for there being margin between cells, so there is 1 fewer margin than cell. We do this by
// imagining there is some non-showed spacing after the final cell. For example with wo cells:
Expand Down
39 changes: 0 additions & 39 deletions BeeSwiftUITests/BeeSwiftUITests.swift

This file was deleted.

Loading