Skip to content

Commit

Permalink
Merge pull request #4 from alexaubry/v1.2.0
Browse files Browse the repository at this point in the history
Release v1.2.0
  • Loading branch information
alexisakers authored Oct 6, 2017
2 parents d046562 + ca1b0ff commit 20abb45
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 25 deletions.
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ before_script:

script:
# Build Framework
- set -o pipefail && xcodebuild clean build -workspace "$WORKSPACE" -scheme "BulletinBoard" -destination "$DESTINATION" | xcpretty
- set -o pipefail && xcodebuild clean build -workspace "$WORKSPACE" -scheme "BulletinBoard" -destination "$DESTINATION" SWIFT_VERSION="3.0" | xcpretty
- set -o pipefail && xcodebuild clean build -workspace "$WORKSPACE" -scheme "BulletinBoard" -destination "$DESTINATION" SWIFT_VERSION="4.0" | xcpretty
# Build Demo Project
- set -o pipefail && xcodebuild clean build -workspace "$WORKSPACE" -scheme "Instanimal" -destination "$DESTINATION" | xcpretty
# Build Project with Package Managers
- carthage build --platform $PLATFORM --no-skip-current
- pod lib lint
- pod lib lint --swift-version=3.0
- pod lib lint --swift-version=4.0
2 changes: 1 addition & 1 deletion BulletinBoard.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "BulletinBoard"
s.version = "1.1.0"
s.version = "1.2.0"
s.summary = "Generate and Display Bottom Card Interfaces for iOS"
s.description = <<-DESC
BulletinBoard is an iOS library that generates and manages contextual cards displayed at the bottom of the screen. It is especially well suited for quick user interactions such as onboarding screens or configuration.
Expand Down
5 changes: 3 additions & 2 deletions BulletinBoard.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
Expand Down Expand Up @@ -286,8 +286,9 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# _BulletinBoard_ Changelog

## 🔖 v1.2.0

- Dismiss the bulletin by swiping down
- Support Swift 3.2

## 🔖 v1.1.0

- Add Accessibility technologies support (VoiceOver, Switch Control) - thanks @lennet!
- Add an optional activity indicator before transitions
- Improve memory management and fix retain cycles/leaks

## 🔖 v1.0.0

- Inital Release
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Here are some screenshots showing what you can build with BulletinBoard:
## Requirements

- iOS 9 and later
- Swift 4
- Swift 3.2 and later

## Demo

Expand Down Expand Up @@ -236,9 +236,9 @@ This creates the following interaction:

![Activity Indicator](https://raw.githubusercontent.com/alexaubry/BulletinBoard/master/.assets/demo_activity.png)

## Automatic Dismissal
## Dismissal

If you set the `isDismissable` property to `true`, the user will be able to dismiss the bulletin by tapping outside of the card.
If you set the `isDismissable` property to `true`, the user will be able to dismiss the bulletin by tapping outside of the card or by swiping the card down.

You should set this property to `true` for the last item.

Expand Down
13 changes: 10 additions & 3 deletions Sources/BulletinInterfaceFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public class BulletinInterfaceFactory {
titleLabel.numberOfLines = 1
titleLabel.adjustsFontSizeToFitWidth = true

titleLabel.font = UIFont.systemFont(ofSize: titleFontSize, weight: .medium)
titleLabel.font = UIFont.systemFont(ofSize: titleFontSize, weight: UIFontWeightMedium)

return titleLabel

Expand Down Expand Up @@ -112,12 +112,12 @@ public class BulletinInterfaceFactory {
actionButton.autoresizingMask = .flexibleWidth

actionButton.setTitle(title, for: .normal)
actionButton.titleLabel?.font = UIFont.systemFont(ofSize: actionButtonFontSize, weight: .semibold)
actionButton.titleLabel?.font = UIFont.systemFont(ofSize: actionButtonFontSize, weight: UIFontWeightSemibold)

actionButton.layer.cornerRadius = 12
actionButton.clipsToBounds = true

let actionContainer = ContainerView(actionButton)
let actionContainer = ContainerView<HighlightButton>(actionButton)
actionContainer.heightAnchor.constraint(equalToConstant: 55).isActive = true

return actionContainer
Expand Down Expand Up @@ -161,3 +161,10 @@ public class BulletinInterfaceFactory {
}

}

// MARK: - Swift Compatibility

#if swift(>=4.0)
private let UIFontWeightMedium = UIFont.Weight.medium
private let UIFontWeightSemibold = UIFont.Weight.semibold
#endif
15 changes: 11 additions & 4 deletions Sources/BulletinManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,24 +181,31 @@ public final class BulletinManager: NSObject, UIViewControllerTransitioningDeleg

/**
* Dismisses the bulletin and clears the current page.
* - parameter animated: Whether to animate dismissal.
*
* - parameter animated: Whether to animate dismissal. Defaults to `true`.
* - parameter completion: An optional block to execute after dismissal. Default to `nil`.
*/

public func dismissBulletin( animated: Bool) {
public func dismissBulletin(animated: Bool = true,
completion: (() -> Void)? = nil) {

precondition(Thread.isMainThread)
precondition(isPrepared, "You must call the `prepare` function before interacting with the bulletin.")

currentItem.tearDown()
currentItem.manager = nil

viewController.dismiss(animated: true) {
viewController.dismiss(animated: animated) {

completion?()

for arrangedSubview in self.viewController.contentStackView.arrangedSubviews {
self.viewController.contentStackView.removeArrangedSubview(arrangedSubview)
arrangedSubview.removeFromSuperview()
}

self.viewController.resetContentView()

}

currentItem = rootItem
Expand All @@ -212,7 +219,7 @@ public final class BulletinManager: NSObject, UIViewControllerTransitioningDeleg

}

/// :nodoc:
/// Returns the presentation controller for the bulletin view controller.
public func presentationController(forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController) -> UIPresentationController? {
Expand Down
92 changes: 87 additions & 5 deletions Sources/BulletinViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import UIKit
* A view controller that displays a card at the bottom of the screen.
*/

final class BulletinViewController: UIViewController {
final class BulletinViewController: UIViewController, UIGestureRecognizerDelegate {

/**
* The stack view displaying the content of the card.
Expand Down Expand Up @@ -50,6 +50,8 @@ final class BulletinViewController: UIViewController {

// MARK: - Private Interface Elements

private var panGesture: UIPanGestureRecognizer!

private let contentView = UIView()
private let activityIndicator = ActivityIndicator()

Expand Down Expand Up @@ -90,10 +92,10 @@ final class BulletinViewController: UIViewController {
centerXConstraint = contentView.centerXAnchor.constraint(equalTo: view.centerXAnchor)

minWidthConstraint = contentView.widthAnchor.constraint(equalToConstant: 444)
minWidthConstraint.priority = .defaultHigh
minWidthConstraint.priority = UILayoutPriorityDefaultHigh

let maxWidthConstraint = contentView.widthAnchor.constraint(lessThanOrEqualTo: view.widthAnchor, constant: -24)
maxWidthConstraint.priority = .required
maxWidthConstraint.priority = UILayoutPriorityRequired
maxWidthConstraint.isActive = true

containerBottomConstraint = contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
Expand All @@ -115,11 +117,11 @@ final class BulletinViewController: UIViewController {

let topConstraint = contentView.topAnchor.constraint(equalTo: contentStackView.topAnchor, constant: -24)
topConstraint.isActive = true
topConstraint.priority = .defaultHigh
topConstraint.priority = UILayoutPriorityDefaultHigh

let minYConstraint = contentView.topAnchor.constraint(greaterThanOrEqualTo: topLayoutGuide.bottomAnchor)
minYConstraint.isActive = true
minYConstraint.priority = .required
minYConstraint.priority = UILayoutPriorityRequired

contentStackView.axis = .vertical
contentStackView.alignment = .fill
Expand All @@ -137,12 +139,22 @@ final class BulletinViewController: UIViewController {

activityIndicator.activityIndicatorViewStyle = .whiteLarge
activityIndicator.color = .black
activityIndicator.isUserInteractionEnabled = true

activityIndicator.alpha = 0

contentView.backgroundColor = #colorLiteral(red: 0.9921568627, green: 1, blue: 1, alpha: 1)
setUpLayout(with: traitCollection)

// Pan Gesture

panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture))
panGesture.maximumNumberOfTouches = 1
panGesture.cancelsTouchesInView = false
panGesture.delegate = self

contentView.addGestureRecognizer(panGesture)

}

// MARK: - Layout
Expand Down Expand Up @@ -239,4 +251,74 @@ final class BulletinViewController: UIViewController {
return dismissIfPossible()
}

// MARK: - Pan Gesture

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return !(touch.view is UIControl)
}

@objc private func handlePanGesture(gestureRecognizer: UIPanGestureRecognizer) {

switch gestureRecognizer.state {
case .began:
gestureRecognizer.setTranslation(.zero, in: contentView)

case .changed:
let translation = gestureRecognizer.translation(in: contentView)
updateContentView(forVerticalTranslation: translation.y)

case .ended:

let translation = gestureRecognizer.translation(in: contentView)
let dismissThreshold = 1/2 * contentView.frame.height

guard translation.y >= dismissThreshold && isDismissable else {

UIView.animate(withDuration: 0.25) {
self.contentView.transform = .identity
}

return

}

dismissIfPossible()

default:
break

}

}

private func updateContentView(forVerticalTranslation translation: CGFloat) {

let translationFactor: CGFloat = translation < 0 ? 1/2 : 2/3

let contentViewTranslation: CGFloat

if translation < 0 || !(isDismissable) {

let frictionTranslation = 30 * atan(translation/120) + translation/10
contentViewTranslation = frictionTranslation * translationFactor

} else {
contentViewTranslation = translation * translationFactor
}

contentView.transform = CGAffineTransform(translationX: 0, y: contentViewTranslation)

}

func resetContentView() {
contentView.transform = .identity
}

}

// MARK: - Swift Compatibility

#if swift(>=4.0)
private let UILayoutPriorityRequired = UILayoutPriority.required
private let UILayoutPriorityDefaultHigh = UILayoutPriority.defaultHigh
#endif
41 changes: 36 additions & 5 deletions Sources/PageBulletinItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,33 @@ open class PageBulletinItem: BulletinItem {

// MARK: - BulletinItem

/// :nodoc:
/**
* The current object managing the item.
*
* This property is set when the item is currently being displayed. It will be set to `nil` when
* the item is removed from view.
*/

public weak var manager: BulletinManager?

/// :nodoc:
/**
* Whether the page can be dismissed.
*
* If you set this value to `true`, the user will be able to dismiss the bulletin by tapping outside
* the card.
*
* You should set it to `true` for the last item you want to display.
*/

public var isDismissable: Bool = false

/// :nodoc:
/**
* The item to display after this one.
*
* If you set this value, you'll be able to call `displayNextItem()` to present the next item to
* the stack.
*/

public var nextItem: BulletinItem? = nil


Expand Down Expand Up @@ -122,7 +142,12 @@ open class PageBulletinItem: BulletinItem {

// MARK: - View Management

/// :nodoc:
/**
* Creates the list of views to display on the bulletin.
*
* This is an implementation detail of `BulletinItem` and you should not call it directly.
*/

public func makeArrangedSubviews() -> [UIView] {

var arrangedSubviews = [UIView]()
Expand Down Expand Up @@ -190,7 +215,13 @@ open class PageBulletinItem: BulletinItem {

}

/// :nodoc:
/**
* Called by the manager when the item was removed from the bulletin view. Use this function
* to remove any button target or gesture recognizers from your managed views.
*
* This is an implementation detail of `BulletinItem` and you should not call it directly.
*/

public func tearDown() {
actionButton?.contentView.removeTarget(self, action: nil, for: .touchUpInside)
alternativeButton?.removeTarget(self, action: nil, for: .touchUpInside)
Expand Down

0 comments on commit 20abb45

Please sign in to comment.