Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for badges to TMBarButton #392

Merged
merged 34 commits into from
Mar 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d82e4d2
Add basic badge view
msaps Feb 22, 2019
de7fa2c
Add appearance properties to badge view
msaps Feb 22, 2019
b109a76
Fix text truncation
msaps Feb 22, 2019
90df868
Add badge layout function
msaps Feb 22, 2019
1f92b63
Add badgeValue to TMBarItem
msaps Feb 22, 2019
dbc99d5
Update access controls
msaps Feb 22, 2019
27fdb96
Update badge appearance
msaps Feb 22, 2019
09d1bc2
Add inset to tab item badge
msaps Feb 23, 2019
7e1ef03
Add default badge text color
msaps Feb 23, 2019
304cc15
Add default badge tint color
msaps Feb 23, 2019
86a1349
Clean up example
msaps Feb 23, 2019
1a82a93
Add tabmanParent to UIViewController
msaps Feb 23, 2019
559f845
Merge branch 'develop' into feature/badges
msaps Feb 23, 2019
4abfbec
Merge branch 'develop' into feature/badges
msaps Mar 3, 2019
04d2d74
Update CHANGELOG
msaps Mar 3, 2019
e01b395
Merge branch 'develop' into feature/badges
msaps Mar 3, 2019
a931f13
Merge branch 'develop' into feature/badges
msaps Mar 3, 2019
fe89aba
Merge branch '2.2.0' into feature/badges
msaps Mar 3, 2019
7701425
Update CHANGELOG
msaps Mar 3, 2019
e644ea6
Add tabmanBarItems to UIViewController
msaps Mar 3, 2019
cf99baf
Make badgeValue settable
msaps Mar 3, 2019
abcc682
Add animation to showing badge
msaps Mar 3, 2019
2c26154
Update docs
msaps Mar 3, 2019
c42e0bd
Improve badge animations
msaps Mar 3, 2019
2355311
Remove badge animations
msaps Mar 3, 2019
188bc9e
Update badge style
msaps Mar 3, 2019
17c89bb
Move badge layout logic
msaps Mar 3, 2019
176aeb2
Fix label bar button badge layout
msaps Mar 3, 2019
b61a12e
Add contentView to badge
msaps Mar 3, 2019
823a820
Improve badge animations
msaps Mar 3, 2019
2d33168
Improve badge animations
msaps Mar 3, 2019
e08e526
Improve badge padding
msaps Mar 3, 2019
bdb2ab4
Update docs
msaps Mar 3, 2019
f1b729a
Restore example app
msaps Mar 3, 2019
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Released on 2019-01-14
- [#363](https://github.com/uias/Tabman/issues/363) `.alignment` property to `TMBarLayout` to adjust content alignment.
- [#378](https://github.com/uias/Tabman/pull/378) `.spacing` property to `TMBarView` to adjust spacing between bar content / accessory views.
- [#373](https://github.com/uias/Tabman/pull/373) `setNeedsUpdate()` to `TMBarItemable` to allow for dynamic item updates.
- [#387](https://github.com/uias/Tabman/issues/387) Ability to show badges on `TMBarButton`.
- `tabmanParent` to `UIViewController`.

#### Updated
- [#378](https://github.com/uias/Tabman/pull/378) Improved bar transitioning behavior when adjusting selected tab.
Expand Down
5 changes: 3 additions & 2 deletions Example/Tabman-Example/ChildViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
//

import UIKit
import Tabman

class ChildViewController: UIViewController {

@IBOutlet private weak var label: UILabel!
@IBOutlet private weak var promptLabel: UILabel!

override var preferredStatusBarStyle: UIStatusBarStyle {
return .default
}
Expand All @@ -23,7 +24,7 @@ class ChildViewController: UIViewController {
}

private func updateIndexLabel() {
if let index = (parentPageboy as? TabPageViewController)?.viewControllers.index(of: self) {
if let index = (pageboyParent as? TabPageViewController)?.viewControllers.index(of: self) {
label.text = "Page " + String(index + 1)

let isFirstPage = index == 0
Expand Down
5 changes: 1 addition & 4 deletions Example/Tabman-Example/TabPageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class TabPageViewController: TabmanViewController {
}

// MARK: PageboyViewControllerDataSource
extension TabPageViewController: PageboyViewControllerDataSource {
extension TabPageViewController: PageboyViewControllerDataSource, TMBarDataSource {

func numberOfViewControllers(in pageboyViewController: PageboyViewController) -> Int {
let count = viewControllers.count
Expand All @@ -183,9 +183,6 @@ extension TabPageViewController: PageboyViewControllerDataSource {
func defaultPage(for pageboyViewController: PageboyViewController) -> PageboyViewController.Page? {
return nil
}
}

extension TabPageViewController: TMBarDataSource {

func barItem(for bar: TMBar, at index: Int) -> TMBarItemable {
return TMBarItem(title: "Page No. \(index + 1)",
Expand Down
16 changes: 16 additions & 0 deletions Sources/Tabman.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
461196EC222004EF0033FE04 /* TMBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461196EB222004EF0033FE04 /* TMBadgeView.swift */; };
4611970E2221B7E70033FE04 /* UIViewController+Tabman.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4611970D2221B7E70033FE04 /* UIViewController+Tabman.swift */; };
4612BCF220C88D24001A37B5 /* TMBar+Templates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4612BCF120C88D24001A37B5 /* TMBar+Templates.swift */; };
4612BD1120C9B851001A37B5 /* BarMath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4612BD1020C9B851001A37B5 /* BarMath.swift */; };
4613CBF2214187EE000E6766 /* CGRect+Interpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4613CBF1214187EE000E6766 /* CGRect+Interpolation.swift */; };
Expand Down Expand Up @@ -76,6 +78,8 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
461196EB222004EF0033FE04 /* TMBadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TMBadgeView.swift; sourceTree = "<group>"; };
4611970D2221B7E70033FE04 /* UIViewController+Tabman.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Tabman.swift"; sourceTree = "<group>"; };
4612BCF120C88D24001A37B5 /* TMBar+Templates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TMBar+Templates.swift"; sourceTree = "<group>"; };
4612BD1020C9B851001A37B5 /* BarMath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarMath.swift; sourceTree = "<group>"; };
4613CBF1214187EE000E6766 /* CGRect+Interpolation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGRect+Interpolation.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -158,6 +162,14 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
461196EA222004E30033FE04 /* Badge */ = {
isa = PBXGroup;
children = (
461196EB222004EF0033FE04 /* TMBadgeView.swift */,
);
path = Badge;
sourceTree = "<group>";
};
4612BD0F20C9B7F5001A37B5 /* Utility */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -221,6 +233,7 @@
isa = PBXGroup;
children = (
464264DC217E12680001C4AF /* PageboyViewController+RelativeCurrentPosition.swift */,
4611970D2221B7E70033FE04 /* UIViewController+Tabman.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -306,6 +319,7 @@
46E96317211B3A01004C7468 /* TMBarButtonInteractionController.swift */,
46E96318211B3A01004C7468 /* TMBarButtonCollection.swift */,
46CCBAD12174BDF600C370E1 /* Types */,
461196EA222004E30033FE04 /* Badge */,
);
path = BarButton;
sourceTree = "<group>";
Expand Down Expand Up @@ -550,6 +564,7 @@
46CCBADC2174BE0900C370E1 /* TMHorizontalBarLayout.swift in Sources */,
462941EB2111EB7C00345B52 /* TMBarView.swift in Sources */,
462F7F3421739D5A0036080C /* TMSystemBar.swift in Sources */,
461196EC222004EF0033FE04 /* TMBadgeView.swift in Sources */,
46CCBAE22174BE1900C370E1 /* TMBarIndicator+None.swift in Sources */,
462F7F31217387E50036080C /* TMBarViewScrollHandler.swift in Sources */,
4612BD1120C9B851001A37B5 /* BarMath.swift in Sources */,
Expand All @@ -572,6 +587,7 @@
46CCBAD72174BDF600C370E1 /* TMBarButton+None.swift in Sources */,
46E96336211B4B06004C7468 /* EdgeFadedView.swift in Sources */,
46196A502179C7C800B51D0F /* UIKit+TMBarItemable.swift in Sources */,
4611970E2221B7E70033FE04 /* UIViewController+Tabman.swift in Sources */,
D62AC0F51E5733FE0020B8AE /* TabmanViewController.swift in Sources */,
46E598EF2168FD3200D8A138 /* TMBarBackgroundView.swift in Sources */,
4628609A213EDB63009C707F /* TMBarViewFocusRect.swift in Sources */,
Expand Down
141 changes: 141 additions & 0 deletions Sources/Tabman/Bar/BarButton/Badge/TMBadgeView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//
// TMBadgeView.swift
// Tabman
//
// Created by Merrick Sapsford on 22/02/2019.
// Copyright © 2019 UI At Six. All rights reserved.
//

import UIKit

/// Badge that displays a label inside a colored eliptical view.
open class TMBadgeView: UIView {

// MARK: Defaults

private struct Defaults {
static let contentInset = UIEdgeInsets(top: 2.0, left: 4.0, bottom: 2.0, right: 4.0)
static let font = UIFont.systemFont(ofSize: 11, weight: .medium)
static let textColor = UIColor.white
static let tintColor = UIColor.red
}

// MARK: Properties

private let contentView = UIView()
private let label = UILabel()

/// Value to display.
internal var value: String? {
didSet {
if value != nil {
label.text = value
}
updateContentVisibility(for: value)
}
}
/// Attributed value to display.
internal var attributedValue: NSAttributedString? {
set {
label.attributedText = newValue
} get {
return label.attributedText
}
}
/// Font for the label.
open var font: UIFont {
set {
label.font = newValue
} get {
return label.font
}
}
/// Text color of the label.
open var textColor: UIColor {
set {
label.textColor = newValue
} get {
return label.textColor
}
}
/// Tint which is used as background color.
open override var tintColor: UIColor! {
didSet {
contentView.backgroundColor = tintColor
}
}

// MARK: Init

public override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}

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

private func initialize() {

addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentView.topAnchor.constraint(equalTo: topAnchor),
trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])

let contentInset = Defaults.contentInset
contentView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: contentInset.left),
label.topAnchor.constraint(equalTo: contentView.topAnchor, constant: contentInset.top),
contentView.trailingAnchor.constraint(equalTo: label.trailingAnchor, constant: contentInset.right),
contentView.bottomAnchor.constraint(equalTo: label.bottomAnchor, constant: contentInset.bottom)
])

NSLayoutConstraint.activate([
widthAnchor.constraint(greaterThanOrEqualTo: heightAnchor)
])

label.textAlignment = .center
label.font = Defaults.font
label.textColor = Defaults.textColor
tintColor = Defaults.tintColor

label.text = "."
updateContentVisibility(for: nil)
}

// MARK: Lifecycle

open override func layoutSubviews() {
super.layoutSubviews()

contentView.layer.cornerRadius = bounds.size.height / 2.0
}

// MARK: Animations

private func updateContentVisibility(for value: String?) {
switch value {
case .none: // hidden
guard contentView.alpha == 1.0 else {
return
}
contentView.alpha = 0.0
contentView.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)

case .some: // visible
guard contentView.alpha == 0.0 else {
return
}
contentView.alpha = 1.0
contentView.transform = .identity
}
}
}
47 changes: 43 additions & 4 deletions Sources/Tabman/Bar/BarButton/TMBarButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import UIKit
/// A button that appears in a bar and provides the interaction for initiating a page index update.
open class TMBarButton: UIControl {

// MARK: Defaults

private struct Defaults {
static let contentInset = UIEdgeInsets.zero
}

// MARK: Types

public enum SelectionState {
Expand All @@ -23,8 +29,8 @@ open class TMBarButton: UIControl {

/// Bar Item that is associated with the button.
public let item: TMBarItemable
private weak var intrinsicSuperview: UIView?

private weak var intrinsicSuperview: UIView?
private let contentView = UIView()
private var contentViewLeading: NSLayoutConstraint!
private var contentViewTop: NSLayoutConstraint!
Expand All @@ -39,7 +45,7 @@ open class TMBarButton: UIControl {
// MARK: Customization

/// Content inset of the button contents.
open var contentInset: UIEdgeInsets = .zero {
open var contentInset: UIEdgeInsets = Defaults.contentInset {
didSet {
contentViewLeading.constant = contentInset.left
contentViewTop.constant = contentInset.top
Expand All @@ -50,6 +56,9 @@ open class TMBarButton: UIControl {
}
}

/// Badge View
public let badge = TMBadgeView()

// MARK: State

/// Selection state of the button.
Expand Down Expand Up @@ -84,6 +93,17 @@ open class TMBarButton: UIControl {

private func initialize() {

layoutBackgroundView()
layoutContentView()

layout(in: contentView)
layoutBadge(badge, in: contentView)
}

// MARK: Layout

private func layoutBackgroundView() {

addSubview(backgroundView)
backgroundView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
Expand All @@ -92,6 +112,9 @@ open class TMBarButton: UIControl {
backgroundView.trailingAnchor.constraint(equalTo: trailingAnchor),
backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}

private func layoutContentView() {

contentView.isUserInteractionEnabled = false
addSubview(contentView)
Expand All @@ -102,18 +125,34 @@ open class TMBarButton: UIControl {
contentViewTrailing = trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
contentViewBottom = bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
NSLayoutConstraint.activate([contentViewLeading, contentViewTop, contentViewTrailing, contentViewBottom])

layout(in: contentView)
}

// MARK: Lifecycle

/// Layout the Bar Button.
///
/// - Parameter view: The view to use as the root of the button.
open func layout(in view: UIView) {
}

/// Layout the badge view for the button.
///
/// - Parameters:
/// - badge: Badge view.
/// - view: View to use for layout.
open func layoutBadge(_ badge: TMBadgeView, in view: UIView) {
}

/// Populate the button with a bar item.
///
/// - Parameter item: Item to populate.
open func populate(for item: TMBarItemable) {
badge.value = item.badgeValue
}

/// Update the button for a new selection state.
///
/// - Parameter selectionState: Selection state.
open func update(for selectionState: SelectionState) {
let minimumAlpha: CGFloat = 0.5
let alpha = minimumAlpha + (selectionState.rawValue * minimumAlpha)
Expand Down
Loading