Skip to content

Commit

Permalink
Merge pull request #1023 from artsy/auction-banner
Browse files Browse the repository at this point in the history
Auction Banner
  • Loading branch information
ashfurrow committed Jan 14, 2016
2 parents 462c7ca + 87818eb commit 1d659ca
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 21 deletions.
4 changes: 4 additions & 0 deletions Artsy.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@
5ECFA8E81907E26E000B92EA /* ARSwitchView+Artist.m in Sources */ = {isa = PBXBuildFile; fileRef = 5ECFA8E71907E26E000B92EA /* ARSwitchView+Artist.m */; };
5ECFA8EA1907E9AB000B92EA /* ARSwitchViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5ECFA8E91907E9AB000B92EA /* ARSwitchViewTests.m */; };
5ECFA8EF1907FC6C000B92EA /* ARSwitchView+FairGuide.m in Sources */ = {isa = PBXBuildFile; fileRef = 5ECFA8EE1907FC6C000B92EA /* ARSwitchView+FairGuide.m */; };
5ED9EC021C458E9500C979A6 /* SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ED9EC011C458E9500C979A6 /* SwiftExtensions.swift */; };
5EDB120B197E691100E241F0 /* ARParallaxHeaderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EDB120A197E691100E241F0 /* ARParallaxHeaderViewController.m */; };
5EE5DE14190167A400040B84 /* ARProfileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EE5DE13190167A400040B84 /* ARProfileViewController.m */; };
5EFE2BE41910FC81003B5EEA /* ARAppDelegate+Analytics.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EFE2BE31910FC81003B5EEA /* ARAppDelegate+Analytics.m */; };
Expand Down Expand Up @@ -1073,6 +1074,7 @@
5ECFA8E91907E9AB000B92EA /* ARSwitchViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARSwitchViewTests.m; sourceTree = "<group>"; };
5ECFA8ED1907FC6C000B92EA /* ARSwitchView+FairGuide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARSwitchView+FairGuide.h"; sourceTree = "<group>"; };
5ECFA8EE1907FC6C000B92EA /* ARSwitchView+FairGuide.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ARSwitchView+FairGuide.m"; sourceTree = "<group>"; };
5ED9EC011C458E9500C979A6 /* SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftExtensions.swift; sourceTree = "<group>"; };
5EDB1209197E691100E241F0 /* ARParallaxHeaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARParallaxHeaderViewController.h; sourceTree = "<group>"; };
5EDB120A197E691100E241F0 /* ARParallaxHeaderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARParallaxHeaderViewController.m; sourceTree = "<group>"; };
5EE5DE12190167A400040B84 /* ARProfileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARProfileViewController.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2489,6 +2491,7 @@
60BC92F21BBEC89B00D13E56 /* ARApplicationShortcutItemDelegate.m */,
607BF5A91C2413810034B519 /* ARAppStatus.h */,
607BF5AA1C2413810034B519 /* ARAppStatus.m */,
5ED9EC011C458E9500C979A6 /* SwiftExtensions.swift */,
);
path = App;
sourceTree = "<group>";
Expand Down Expand Up @@ -4609,6 +4612,7 @@
60E6447617BE424E004486B3 /* ARSwitchView.m in Sources */,
3CF144A418E3727F00B1A764 /* UIViewController+ScreenSize.m in Sources */,
603B55A117D64A1B00566935 /* ARArtworkPriceView.m in Sources */,
5ED9EC021C458E9500C979A6 /* SwiftExtensions.swift in Sources */,
6001414817CA33C100612DB4 /* ARArtworkRelatedArtworksView.m in Sources */,
607D754A17C239E700CA1D41 /* ArtsyAPI+Following.m in Sources */,
E67655261900660500F9A704 /* ARWhitespaceGobbler.m in Sources */,
Expand Down
6 changes: 6 additions & 0 deletions Artsy/App/SwiftExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// Given a closure T -> Void, returns a function that takes a T and invokes the closure.
/// Useful for performing operations with curried functions.
/// Ex: [a, b, c,].forEach(apply(self.function)) will call self.function with a, b, then c as parameters.
func apply<T>(closure: T -> Void)(instance: T) {
closure(instance)
}
2 changes: 2 additions & 0 deletions Artsy/Resources/Artsy-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#import "ARScrollNavigationChief.h"
#import "ARWhitespaceGobbler.h"
#import "UIDevice-Hardware.h"
#import "ARCountdownView.h"

// Models. Importing Models.h is a no-go, since each header implicitly relies on a bunch of stuff imported from the PCH.
#import "Sale.h"
5 changes: 3 additions & 2 deletions Artsy/View_Controllers/Auction/AuctionNetworkModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ class AuctionNetworkModel {
self.saleID = saleID
}

func fetchSale(callback: Result<Sale> -> Void) {
func fetchSale(callback: Result<SaleViewModel> -> Void) {
ArtsyAPI.getSaleWithID(saleID,
success: { sale in
callback(.Success(sale))
let saleViewModel = SaleViewModel(sale: sale)
callback(.Success(saleViewModel))
},
failure: { error in
callback(.Failure(error))
Expand Down
25 changes: 14 additions & 11 deletions Artsy/View_Controllers/Auction/AuctionViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class AuctionViewController: UIViewController {

var stackScrollView: ORStackScrollView!

var willAppearToken: dispatch_once_t = 0

lazy var networkModel: AuctionNetworkModel = {
return AuctionNetworkModel(saleID: self.saleID)
}()
Expand Down Expand Up @@ -38,15 +40,17 @@ class AuctionViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)

ar_presentIndeterminateLoadingIndicatorAnimated(animated)
networkModel.fetchSale { result in
self.ar_removeIndeterminateLoadingIndicatorAnimated(animated)
dispatch_once(&willAppearToken) { () -> Void in
self.ar_presentIndeterminateLoadingIndicatorAnimated(animated)
self.networkModel.fetchSale { result in
self.ar_removeIndeterminateLoadingIndicatorAnimated(animated)

switch result {
case .Success(let sale):
self.setupForSale(sale)
case .Failure(_):
break // TODO: How to handle error?
switch result {
case .Success(let saleViewModel):
self.setupForSale(saleViewModel)
case .Failure(_):
break // TODO: How to handle error?
}
}
}
}
Expand All @@ -59,16 +63,15 @@ class AuctionViewController: UIViewController {
}

extension AuctionViewController {
func setupForSale(sale: Sale) {
let saleViewModel = SaleViewModel(sale: sale)
func setupForSale(saleViewModel: SaleViewModel) {
self.saleViewModel = saleViewModel

[ (AuctionBannerView(viewModel: saleViewModel), ViewTags.Banner),
(AuctionTitleView(viewModel: saleViewModel), .Title),
(ARWhitespaceGobbler(), .WhitespaceGobbler)
].forEach { (view, tag) in
view.tag = tag.rawValue
self.stackScrollView.stackView.addSubview(view, withTopMargin: "0")
self.stackScrollView.stackView.addSubview(view, withTopMargin: "0", sideMargin: "0")
}
}
}
7 changes: 7 additions & 0 deletions Artsy/Views/Artwork/Auctions/ARCountdownView.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@


@interface ARCountdownView : UIView

/// Set up countdown view with a specific colour, applied to all labels.
- (instancetype)initWithColor:(UIColor *)color;

/// Set up countdown view with basic, default colours. Useful over white backgrounds.
- (instancetype)init;

- (void)startTimer;
- (void)stopTimer;

Expand Down
18 changes: 12 additions & 6 deletions Artsy/Views/Artwork/Auctions/ARCountdownView.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,34 @@ @interface ARCountdownView ()

@implementation ARCountdownView

- (instancetype)init
- (instancetype)initWithColor:(UIColor *)color
{
self = [super init];
if (self) {
[self setupSubviews];
[self setupSubviewsWithColor:color];
}
return self;
}

- (void)setupSubviews
- (instancetype)init
{
return [self initWithColor:nil];
}

- (void)setupSubviewsWithColor:(UIColor *)color
{
self.headingLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.headingLabel.textAlignment = NSTextAlignmentCenter;
self.headingLabel.font = [UIFont smallCapsSerifFontWithSize:14];
self.headingLabel.textColor = [UIColor blackColor];
self.headingLabel.textColor = color ?: [UIColor blackColor];

[self addSubview:self.headingLabel];
[self.headingLabel alignTopEdgeWithView:self predicate:@"0"];
[self.headingLabel alignCenterXWithView:self predicate:@"0"];

self.countdown = [[UILabel alloc] initWithFrame:CGRectZero];
self.countdown.font = [UIFont sansSerifFontWithSize:20];
self.countdown.textColor = [UIColor blackColor];
self.countdown.textColor = color ?: [UIColor blackColor];
self.countdown.text = [self countdownString];
self.countdown.textAlignment = NSTextAlignmentCenter;

Expand All @@ -45,12 +50,13 @@ - (void)setupSubviews
[labelsContainer constrainTopSpaceToView:self.countdown predicate:@"0"];
[labelsContainer alignCenterXWithView:self predicate:@"0"];
[labelsContainer alignBottomEdgeWithView:self predicate:@"0"];
[labelsContainer alignLeading:@"0" trailing:@"0" toView:self];

NSMutableArray *labels = [NSMutableArray array];
[@[ @"Days", @"Hrs", @"Min", @"Sec" ] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = [obj uppercaseString];
label.textColor = [UIColor artsyHeavyGrey];
label.textColor = color ?: [UIColor artsyHeavyGrey];
label.font = [UIFont sansSerifFontWithSize:8];
label.textAlignment = NSTextAlignmentCenter;
[labelsContainer addSubview:label];
Expand Down
80 changes: 79 additions & 1 deletion Artsy/Views/Auction/AuctionBannerView.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,94 @@
import UIKit
import Artsy_UIColors
import FLKAutoLayout
import SDWebImage

class AuctionBannerView: UIView {
let viewModel: SaleViewModel

// Note: These are in order as they'll be in the view hierarchy (ie: first in the list is at the back)
private let backgroundImageView = UIImageView()
private let darkeningView = DarkeningView()
private let logoImageView = UIImageView()
private let countdownView = ARCountdownView(color: .whiteColor())

init(viewModel: SaleViewModel) {
self.viewModel = viewModel
super.init(frame: CGRect.zero)

backgroundColor = .redColor()
setupViews()
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func willMoveToSuperview(newSuperview: UIView?) {
super.willMoveToSuperview(newSuperview)

// Countdown view only counts down when we have a superview.
if let _ = newSuperview {
countdownView.startTimer()
} else {
countdownView.stopTimer()
}
}
}

extension AuctionBannerView {
private func setupViews() {

// Configure views.
countdownView.targetDate = viewModel.closingDate
countdownView.heading = "Closing In"

// Add all as subviews to self.
[backgroundImageView, darkeningView, logoImageView, countdownView].forEach(apply(addSubview))

// Background + darkening view always cover self totally.
backgroundImageView.alignToView(self)
darkeningView.alignToView(self)

logoImageView.constrainHeight("70")

// Device-specific layout for logo & countdown views.
if UIDevice.isPad() {
// Bottom lefthand corner with 40pt margin.
logoImageView.alignLeadingEdgeWithView(self, predicate: "40")
logoImageView.alignBottomEdgeWithView(self, predicate: "-40")

// Must constraint self to 200pt tall on iPad, since our view hierarchy doesn't provide any height constraint and in its abses, defaults to the background image's height.
constrainHeight("200")

// Bottom righthand corner with 40pt margin.
countdownView.alignTrailingEdgeWithView(self, predicate: "-40")
countdownView.alignBottomEdgeWithView(self, predicate: "-40")
} else {
logoImageView.alignTopEdgeWithView(self, predicate: "30")
countdownView.constrainTopSpaceToView(logoImageView, predicate: "7")
countdownView.alignBottomEdgeWithView(self, predicate: "-30")

// The background will stretch us to be larger (based on its image height), so we want to prevent that.
backgroundImageView.setContentCompressionResistancePriority(UILayoutPriorityDefaultLow, forAxis: .Vertical)

logoImageView.alignCenterXWithView(self, predicate: "0")
countdownView.alignCenterXWithView(self, predicate: "0")
}

// Start any necessary image downloads.
backgroundImageView.sd_setImageWithURL(viewModel.backgroundImageURL)
logoImageView.sd_setImageWithURL(viewModel.profileImageURL) { [weak logoImageView] (image, _, _, _) in
// This keeps the image view constrained to the image's aspect ratio, which allows us to 'left align' this on iPad.
let aspectRatio = image.size.width / image.size.height
logoImageView?.constrainAspectRatio("\(aspectRatio)")
}
}
}

private class DarkeningView: UIView {
private override func didMoveToSuperview() {
super.didMoveToSuperview()

backgroundColor = UIColor(white: 0, alpha: 0.3)
}
}
12 changes: 11 additions & 1 deletion Artsy/Views/Auction/SaleViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,15 @@ class SaleViewModel {
}

extension SaleViewModel {

var backgroundImageURL: NSURL! {
return NSURL(string: "https://d32dm0rphc51dk.cloudfront.net/BLv_dHIIVvShtDB8GCxFdg/large_rectangle.jpg")!
}

var profileImageURL: NSURL! {
return NSURL(string: "https://d32dm0rphc51dk.cloudfront.net/n9QgQtio1Rrp-vaKGJH7aA/square140.png")
}

var closingDate: NSDate {
return sale.endDate
}
}
1 change: 1 addition & 0 deletions CHANGELOG.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ upcoming:
- Added Dangerfile to the repo - orta
- Send dsym to hockey - orta
- Converted CHANGELOG to yaml - orta
- New (native) auction banner view - ash

releases:
- version: 2.3.3
Expand Down

0 comments on commit 1d659ca

Please sign in to comment.