Skip to content

Commit

Permalink
Fix #11
Browse files Browse the repository at this point in the history
  • Loading branch information
aydenp committed Dec 4, 2017
1 parent 00079f0 commit 1339a50
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 16 deletions.
4 changes: 4 additions & 0 deletions Bank.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
C31107CF1FD507B600ABF56B /* Account+Transactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31107CE1FD507B600ABF56B /* Account+Transactions.swift */; };
C31107D11FD51C3700ABF56B /* HairlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31107D01FD51C3700ABF56B /* HairlineView.swift */; };
C31107D31FD51C4F00ABF56B /* StatusBarOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31107D21FD51C4F00ABF56B /* StatusBarOverlayView.swift */; };
C31107D51FD52F0200ABF56B /* RangeSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31107D41FD52F0200ABF56B /* RangeSegmentedControl.swift */; };
C316128E1FD48B5D000EC5ED /* InstitutionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = C316128D1FD48B5D000EC5ED /* InstitutionResponse.swift */; };
C31612901FD48B7E000EC5ED /* Institution.swift in Sources */ = {isa = PBXBuildFile; fileRef = C316128F1FD48B7E000EC5ED /* Institution.swift */; };
C31612961FD4EDB9000EC5ED /* NSAttributedString+LargestFontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31612951FD4EDB9000EC5ED /* NSAttributedString+LargestFontSize.swift */; };
Expand Down Expand Up @@ -57,6 +58,7 @@
C31107CE1FD507B600ABF56B /* Account+Transactions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Transactions.swift"; sourceTree = "<group>"; };
C31107D01FD51C3700ABF56B /* HairlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HairlineView.swift; sourceTree = "<group>"; };
C31107D21FD51C4F00ABF56B /* StatusBarOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarOverlayView.swift; sourceTree = "<group>"; };
C31107D41FD52F0200ABF56B /* RangeSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeSegmentedControl.swift; sourceTree = "<group>"; };
C316128D1FD48B5D000EC5ED /* InstitutionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstitutionResponse.swift; sourceTree = "<group>"; };
C316128F1FD48B7E000EC5ED /* Institution.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Institution.swift; sourceTree = "<group>"; };
C31612951FD4EDB9000EC5ED /* NSAttributedString+LargestFontSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+LargestFontSize.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -235,6 +237,7 @@
C3CBCABC1FD4767A00010AA8 /* AccountHeaderView.swift */,
C3CBCABA1FD469B900010AA8 /* BackgroundGradientView.swift */,
C31107D21FD51C4F00ABF56B /* StatusBarOverlayView.swift */,
C31107D41FD52F0200ABF56B /* RangeSegmentedControl.swift */,
C3CBCAB71FD461B600010AA8 /* Utility */,
);
path = Views;
Expand Down Expand Up @@ -387,6 +390,7 @@
C3CBCAA71FD3186700010AA8 /* ViewController+Status.swift in Sources */,
C3F578021FD2DD8C0017EE17 /* PlaidResponse.swift in Sources */,
C3BCF72A1FCF5C130010497E /* ViewController.swift in Sources */,
C31107D51FD52F0200ABF56B /* RangeSegmentedControl.swift in Sources */,
C3CBCA951FD3075D00010AA8 /* AccessTokenExchangeResponse.swift in Sources */,
C3CBCAAB1FD3663F00010AA8 /* PlaidAPI+Transactions.swift in Sources */,
C3CBCAC11FD4895000010AA8 /* PaginatingPlaidResponse.swift in Sources */,
Expand Down
45 changes: 35 additions & 10 deletions Bank/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,43 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<view key="tableHeaderView" contentMode="scaleToFill" id="BCK-rc-BfJ" customClass="AccountHeaderView" customModule="Bank" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="360"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="370"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="drY-yl-DdM">
<rect key="frame" x="0.0" y="192.5" width="375" height="167.5"/>
<rect key="frame" x="0.0" y="192.5" width="375" height="177.5"/>
<subviews>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="abw-PF-ErW">
<rect key="frame" x="177" y="74" width="20" height="20"/>
</activityIndicatorView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NyN-jc-QkS">
<rect key="frame" x="0.0" y="-0.5" width="375" height="134"/>
<subviews>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="abw-PF-ErW">
<rect key="frame" x="177.5" y="57.5" width="20" height="20"/>
</activityIndicatorView>
</subviews>
<constraints>
<constraint firstItem="abw-PF-ErW" firstAttribute="centerY" secondItem="NyN-jc-QkS" secondAttribute="centerY" id="D98-wx-emL"/>
<constraint firstItem="abw-PF-ErW" firstAttribute="centerX" secondItem="NyN-jc-QkS" secondAttribute="centerX" id="d82-KF-4Ap"/>
</constraints>
</view>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="Hae-2f-zgd" customClass="RangeSegmentedControl" customModule="Bank" customModuleProvider="target">
<rect key="frame" x="14" y="143.5" width="347" height="29"/>
<segments>
<segment title="Value 1"/>
<segment title="Value 2"/>
</segments>
<connections>
<action selector="segmentedControlValueChanged:" destination="BCK-rc-BfJ" eventType="valueChanged" id="ijd-HJ-ofQ"/>
</connections>
</segmentedControl>
</subviews>
<constraints>
<constraint firstItem="abw-PF-ErW" firstAttribute="centerX" secondItem="drY-yl-DdM" secondAttribute="centerX" id="Vl5-L5-WtE"/>
<constraint firstItem="abw-PF-ErW" firstAttribute="centerY" secondItem="drY-yl-DdM" secondAttribute="centerY" id="njA-Hm-ujt"/>
<constraint firstAttribute="trailing" secondItem="Hae-2f-zgd" secondAttribute="trailing" constant="14" id="3hT-lk-bvd"/>
<constraint firstAttribute="trailing" secondItem="NyN-jc-QkS" secondAttribute="trailing" id="8qu-Jd-be2"/>
<constraint firstItem="Hae-2f-zgd" firstAttribute="leading" secondItem="drY-yl-DdM" secondAttribute="leading" constant="14" id="AwD-Mw-DBK"/>
<constraint firstItem="Hae-2f-zgd" firstAttribute="top" secondItem="NyN-jc-QkS" secondAttribute="bottom" constant="10" id="C9n-WW-FPe"/>
<constraint firstAttribute="bottom" secondItem="Hae-2f-zgd" secondAttribute="bottom" constant="6" id="HbN-UD-2hL"/>
<constraint firstItem="NyN-jc-QkS" firstAttribute="leading" secondItem="drY-yl-DdM" secondAttribute="leading" id="PBP-fy-U27"/>
<constraint firstItem="NyN-jc-QkS" firstAttribute="top" secondItem="drY-yl-DdM" secondAttribute="top" id="T3M-1k-sNS"/>
</constraints>
</view>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="UDC-Ph-GPI">
Expand Down Expand Up @@ -226,21 +250,22 @@
</constraints>
<connections>
<outlet property="amountLabel" destination="KUA-aH-omm" id="Q2X-oC-uXQ"/>
<outlet property="chartContainerView" destination="drY-yl-DdM" id="uZO-aA-w7i"/>
<outlet property="chartContainerView" destination="NyN-jc-QkS" id="lzB-Lr-cfK"/>
<outlet property="chartLoadingView" destination="abw-PF-ErW" id="mFm-e9-giY"/>
<outlet property="chartSegmentedControl" destination="Hae-2f-zgd" id="wiV-3i-Ttn"/>
<outlet property="infoStackView" destination="UDC-Ph-GPI" id="Cqz-6j-UcJ"/>
<outlet property="institutionLabel" destination="5Gl-gL-Tu5" id="TKl-d7-QwK"/>
<outlet property="nameLabel" destination="Plo-Ia-DOK" id="W3J-qN-XBK"/>
<outlet property="pageControl" destination="7N1-VT-en8" id="5RI-lX-7y0"/>
</connections>
</view>
<view key="tableFooterView" contentMode="scaleToFill" id="5ty-Ac-eji">
<rect key="frame" x="0.0" y="432" width="375" height="1"/>
<rect key="frame" x="0.0" y="442" width="375" height="1"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</view>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Transaction" id="f32-jP-FIA" customClass="TransactionCell" customModule="Bank" customModuleProvider="target">
<rect key="frame" x="0.0" y="388" width="375" height="44"/>
<rect key="frame" x="0.0" y="398" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="f32-jP-FIA" id="zc7-a0-ArG">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
Expand Down
7 changes: 4 additions & 3 deletions Bank/Models/Account+Transactions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ extension Account {
return SessionDataStorage.shared.transactions(for: self)
}

func getBalanceHistoricalData(from startDate: Date, completionHandler: @escaping ([(Date, Double)]) -> Void) {
func getBalanceHistoricalData(from startDate: Date? = nil, completionHandler: @escaping ([(Date, Double)]) -> Void) {
OperationQueue().addOperation {
let transactions = self.transactions
// Get transactions, oldest first
let transactions = self.transactions.sorted { $0.date < $1.date }
var values = [(Date, Double)]()
var currentDate = startDate
let now = Date()
var currentDate = startDate ?? transactions.first?.date ?? now
var currentBalance = self.displayBalance
// Loop through all dates between the start date and now
while currentDate <= now {
Expand Down
2 changes: 2 additions & 0 deletions Bank/SessionDataStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class SessionDataStorage {
}
}

var selectedChartRange: AccountHeaderView.ChartViewRange = .defaultOption

func transactions(for account: Account) -> [Transaction] {
return transactions?.filter { $0.accountID == account.id } ?? []
}
Expand Down
51 changes: 48 additions & 3 deletions Bank/Views/AccountHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,45 @@ class AccountHeaderView: UIView {
@IBOutlet weak var institutionLabel: UILabel!
@IBOutlet weak var chartContainerView: UIView!
@IBOutlet weak var chartLoadingView: UIActivityIndicatorView!
@IBOutlet weak var chartSegmentedControl: UISegmentedControl!
@IBOutlet weak var pageControl: UIPageControl!
@IBOutlet weak var infoStackView: UIStackView!
var hasAwaken = false
var chart: Chart!

enum ChartViewRange: TimeInterval {
static let allOptions: [ChartViewRange] = [.week, .twoWeeks, .month, .threeMonths, .sixMonths, .year, .all]
static let defaultOption = ChartViewRange.month
case week = 7, twoWeeks = 14, month = 30, threeMonths = 90, sixMonths = 180, year = 365, all = 0

var startDate: Date? {
guard days > 0 else { return nil }
return Date().addingTimeInterval(-60 * 60 * 24 * days)
}

var days: TimeInterval {
return rawValue
}

var displayText: String {
switch self {
case .week: return "1W"
case .twoWeeks: return "2W"
case .month: return "1M"
case .threeMonths: return "3M"
case .sixMonths: return "6M"
case .year: return "1Y"
case .all: return "All"
}
}
}

override func awakeFromNib() {
super.awakeFromNib()
// Setup chart
chart = Chart(frame: chartContainerView.bounds)
chart.autoresizingMask = [.flexibleWidth, .flexibleHeight]
chart.clipsToBounds = true
chart.gridColor = .clear
chart.axesColor = .clear
chart.highlightLineColor = .lightGray
Expand All @@ -35,13 +64,24 @@ class AccountHeaderView: UIView {
chart.topInset = 0
chart.bottomInset = 0
chartContainerView.addSubview(chart)
chartSegmentedControl.removeAllSegments()
ChartViewRange.allOptions.enumerated().forEach {
chartSegmentedControl.insertSegment(withTitle: $0.element.displayText.uppercased(), at: $0.offset, animated: false)
if $0.element == SessionDataStorage.shared.selectedChartRange {
chartSegmentedControl.selectedSegmentIndex = $0.offset
}
}
// Initialization code
setupData()
hasAwaken = true
// Listen for changes to periodic institution data store
NotificationCenter.default.addObserver(self, selector: #selector(setupData), name: PeriodicFetchDataStorage.shared.institutions.dataChangedNotification, object: nil)
}

private var selectedRange: ChartViewRange {
return ChartViewRange.allOptions[chartSegmentedControl.selectedSegmentIndex]
}

var account: Account? {
didSet {
if hasAwaken { setupData() }
Expand All @@ -68,23 +108,28 @@ class AccountHeaderView: UIView {
chartLoadingView.stopAnimating()
guard let account = account else { return }
chartLoadingView.startAnimating()
account.getBalanceHistoricalData(from: Date().addingTimeInterval(-60 * 60 * 24 * 30)) { (data) in
account.getBalanceHistoricalData(from: selectedRange.startDate) { (data) in
DispatchQueue.main.async {
let series = ChartSeries(data: data.map { (x: Float($0.0.timeIntervalSinceReferenceDate), y: Float($0.1)) })
series.color = .gradientColor(from: Colours.lighterMain, to: Colours.main, height: self.chartContainerView.bounds.height)
series.color = Colours.main
self.chart.add(series)
self.chartLoadingView.stopAnimating()
}
}
}

// MARK: - Event Handlers

@IBAction func pageControlChanged(_ sender: Any) {
delegate?.shouldMove(to: pageControl.currentPage)
pageControl.currentPage = accountIndex?.0 ?? 0
}

@IBAction func segmentedControlValueChanged(_ sender: Any) {
setupChart()
SessionDataStorage.shared.selectedChartRange = selectedRange
}

}

protocol AccountHeaderViewDelegate: class {
Expand Down
74 changes: 74 additions & 0 deletions Bank/Views/RangeSegmentedControl.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// RangeSegmentedControl.swift
// Bank
//
// Created by Ayden Panhuyzen on 2017-12-04.
// Copyright © 2017 Ayden Panhuyzen. All rights reserved.
//

import UIKit

class RangeSegmentedControl: UISegmentedControl {
private var selectionView: UIView!, selectionViewLeftAnchor: NSLayoutConstraint!, selectionViewWidthAnchor: NSLayoutConstraint!

override init(frame: CGRect) {
super.init(frame: frame)
setup()
}

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

private func setup() {
heightAnchor.constraint(equalToConstant: 34).isActive = true
selectionView = UIView()
selectionView.translatesAutoresizingMaskIntoConstraints = false
addSubview(selectionView)
selectionView.heightAnchor.constraint(equalToConstant: 2).isActive = true
selectionViewLeftAnchor = selectionView.leftAnchor.constraint(equalTo: leftAnchor)
selectionViewLeftAnchor.isActive = true
selectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
selectionViewWidthAnchor = selectionView.widthAnchor.constraint(equalToConstant: 0)
selectionViewWidthAnchor.isActive = true
setColours()
addTarget(self, action: #selector(didChangeIndex), for: .valueChanged)
}

override func tintColorDidChange() {
super.tintColorDidChange()
setColours()
}

override func layoutSubviews() {
super.layoutSubviews()
moveSelectionView()
}

@objc func didChangeIndex() {
self.moveSelectionView(animated: true)
}

var isAnimating = false
private func moveSelectionView(animated: Bool = false, overrideAnimationState: Bool = false) {
if isAnimating && !overrideAnimationState { return }
if animated {
isAnimating = true
UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.85, initialSpringVelocity: 1, options: [], animations: {
self.moveSelectionView(animated: false, overrideAnimationState: true)
}, completion: { _ in self.isAnimating = false })
return
}
selectionViewWidthAnchor.constant = bounds.size.width / CGFloat(numberOfSegments)
selectionViewLeftAnchor.constant = selectionViewWidthAnchor.constant * CGFloat(selectedSegmentIndex)
layoutIfNeeded()
}

private func setColours() {
tintColor = .clear
setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor(white: 0.7569, alpha: 1), NSAttributedStringKey.font: UIFont.systemFont(ofSize: 15, weight: .bold)], for: .normal)
setTitleTextAttributes([NSAttributedStringKey.foregroundColor: Colours.main], for: .selected)
selectionView.backgroundColor = Colours.main
}
}

0 comments on commit 1339a50

Please sign in to comment.