Skip to content

Commit

Permalink
Task/card part icon label (#164)
Browse files Browse the repository at this point in the history
* Implementation of cardpart icon label

* Added readme for cardpartIcon Label

* Updsted set up to handle corner radius aspect

* Added ninding for icon adopted review comment
  • Loading branch information
badrinathvm authored and croossin committed Aug 23, 2019
1 parent 42e659a commit 3c9fa78
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 1 deletion.
177 changes: 177 additions & 0 deletions CardParts/src/Classes/Card Parts/CardPartIconLabel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//
// CardPartIconLabel.swift
// CardParts
//
// Created by Venkatnarayansetty, Badarinath on 8/20/19.
//

import Foundation
import UIKit
import RxSwift
import RxCocoa

public class CardPartIconLabel: UILabel, CardPartView {

public enum HorizontalPosition {
case left
case right
}

public enum VerticalPosition {
case top
case center
case bottom
}

public var margins: UIEdgeInsets = CardParts.theme.cardPartMargins

/// provides vertical and horizontal spacing
public var verticalPadding:CGFloat = 2.0
public var horizontalPadding:CGFloat = 2.0
public var padding:CGFloat = 10.0

public var iconView: UIImageView? {
didSet {
guard let image = iconView?.image else { return }
self.icon = image
}
}

/// Horizontal and vertical position
public typealias Position = (horizontal: CardPartIconLabel.HorizontalPosition, vertical: CardPartIconLabel.VerticalPosition)
open var iconPosition: Position = (.left , .top)

/// additional spacing between text and the image
public var iconPadding: CGFloat = 0

public var icon:UIImage? {
didSet {
if icon == nil {
iconView?.removeFromSuperview()
}
setNeedsDisplay()
}
}

public var labelText: String? {
didSet {
guard let text = labelText else { return }
self.text = text
}
}

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

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

fileprivate func setup() {
self.clipsToBounds = true
}

open override func drawText(in rect: CGRect) {
guard let text = text as NSString? else { return }

guard let icon = icon else {
super.drawText(in: rect)
return
}

//remove from view if it's present before adding it.
iconView?.removeFromSuperview()
iconView = UIImageView(image: icon)

//calculate frame of the text based on content.
let size = text.boundingRect(with: CGSize(width: frame.width - icon.size.width - iconPadding, height: .greatestFiniteMagnitude),
options: NSStringDrawingOptions.usesLineFragmentOrigin,
attributes: [.font : font as Any ],
context: nil).size

var newRect = CGRect.zero
guard let iconView = iconView else { return }

/// calculate the icon y position
let iconYPosition = (frame.height - iconView.frame.height) / 2
let height = frame.height

// set the frame positions according to text alignment and icon horizontal positions.
switch iconPosition.horizontal {
case .left:
if textAlignment == .left {
iconView.frame = iconView.frame.offsetBy(dx: padding, dy: iconYPosition)
newRect = CGRect(x: iconView.frame.width + iconPadding + padding, y: 0, width: frame.width - (iconView.frame.width + iconPadding), height: height)
}else if textAlignment == .right {
iconView.frame = iconView.frame.offsetBy(dx: frame.width - size.width - iconPadding - iconView.frame.width - padding, dy: iconYPosition)
newRect = CGRect(x: frame.width - size.width - iconPadding - padding , y: 0, width: size.width + iconPadding, height: height)
}else if textAlignment == .center {
iconView.frame = iconView.frame.offsetBy(dx: (frame.width - size.width) / 2 - iconPadding - iconView.frame.width, dy: iconYPosition)
newRect = CGRect(x: (frame.width - size.width) / 2 , y: 0, width: size.width + iconPadding, height: height)
}
case .right:
if textAlignment == .left {
iconView.frame = iconView.frame.offsetBy(dx: size.width + iconPadding + padding, dy: iconYPosition)
newRect = CGRect(x: 10, y: 0, width: size.width, height: height)
}else if textAlignment == .right {
iconView.frame = iconView.frame.offsetBy(dx: frame.width - iconView.frame.width - padding , dy: iconYPosition)
newRect = CGRect(x: frame.width - size.width - iconView.frame.width - iconPadding - 2 * padding, y: 0, width: size.width + iconPadding, height: height)
}else if textAlignment == .center {
iconView.frame = iconView.frame.offsetBy(dx: frame.width / 2 + size.width / 2 + iconPadding, dy: iconYPosition)
newRect = CGRect(x: (frame.width - size.width) / 2, y: 0, width: size.width, height: height)
}
}

// set the frame positions of the icon vertically i.e., top, bottom or center
switch iconPosition.vertical {
case .top: iconView.frame.origin.y = (frame.height - size.height) / 2

case .center: iconView.frame.origin.y = (frame.height - iconView.frame.height) / 2

case .bottom: iconView.frame.origin.y = frame.height - (frame.height - size.height) / 2 - iconView.frame.size.height
}

addSubview(iconView)
super.drawText(in: newRect)
setup()
}

/// returns new size with updated vertical and horizantal padding.
public override var intrinsicContentSize: CGSize {
let superSize = super.intrinsicContentSize
let newWidth = superSize.width + superSize.height + (2 * horizontalPadding)
let newHeight = superSize.height + (2 * verticalPadding)
let size = CGSize(width: newWidth, height: newHeight)
return size
}
}

// MARK: - Reactive binding for label's text , vertical & horizontal padding.
extension Reactive where Base: CardPartIconLabel {

public var labelText: Binder<String?>{
return Binder(self.base) { (label, labelText) -> () in
label.text = labelText
}
}

public var verticalPadding: Binder<CGFloat> {
return Binder(self.base) { (label, verticalPadding) -> () in
label.verticalPadding = verticalPadding
}
}

public var horizontalPadding: Binder<CGFloat> {
return Binder(self.base) { (label, horizontalPadding) -> () in
label.horizontalPadding = horizontalPadding
}
}

public var iconView: Binder<UIImageView> {
return Binder(self.base) { (imageView, iconView) -> () in
imageView.iconView = iconView
}
}
}
4 changes: 4 additions & 0 deletions Example/CardParts.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
1964FC2A3640A8318B06FAE2 /* Pods_CardParts_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 411FA9F08E0EF1AF5B1D8EA1 /* Pods_CardParts_Tests.framework */; };
1B1E203D22F47A8700734EDA /* CardPartPillLabelCardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B1E203C22F47A8700734EDA /* CardPartPillLabelCardController.swift */; };
1B1E204022F4ADE900734EDA /* CardPartRoundedStackViewCardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B1E203F22F4ADE900734EDA /* CardPartRoundedStackViewCardController.swift */; };
1B693BA1230DAE2900662382 /* CardPartIconLabelCardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B693BA0230DAE2900662382 /* CardPartIconLabelCardController.swift */; };
1BD1074022F22823000163D2 /* CardPartBorderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD1073F22F22823000163D2 /* CardPartBorderViewController.swift */; };
5570914320D18B07004D8E5A /* CardPartOrientedViewCardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5570914220D18B07004D8E5A /* CardPartOrientedViewCardController.swift */; };
55809E2620DABBF3008BE0D2 /* CardPartCenteredViewCardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55809E2520DABBF3008BE0D2 /* CardPartCenteredViewCardController.swift */; };
Expand Down Expand Up @@ -60,6 +61,7 @@
/* Begin PBXFileReference section */
1B1E203C22F47A8700734EDA /* CardPartPillLabelCardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPartPillLabelCardController.swift; sourceTree = "<group>"; };
1B1E203F22F4ADE900734EDA /* CardPartRoundedStackViewCardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPartRoundedStackViewCardController.swift; sourceTree = "<group>"; };
1B693BA0230DAE2900662382 /* CardPartIconLabelCardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPartIconLabelCardController.swift; sourceTree = "<group>"; };
1BD1073F22F22823000163D2 /* CardPartBorderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPartBorderViewController.swift; sourceTree = "<group>"; };
2BA2B86E3C2946F91C18390D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
2C0391CB2C9A48987E449BE1 /* Pods-CardParts_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CardParts_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CardParts_Tests/Pods-CardParts_Tests.debug.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -237,6 +239,7 @@
1BD1073F22F22823000163D2 /* CardPartBorderViewController.swift */,
1B1E203C22F47A8700734EDA /* CardPartPillLabelCardController.swift */,
1B1E203F22F4ADE900734EDA /* CardPartRoundedStackViewCardController.swift */,
1B693BA0230DAE2900662382 /* CardPartIconLabelCardController.swift */,
);
name = "Type of CardParts";
sourceTree = "<group>";
Expand Down Expand Up @@ -494,6 +497,7 @@
7051279F20B5E2ED00B8E635 /* CardPartTextFieldCardController.swift in Sources */,
70BB3BF420FF89A500F55D57 /* CardPartTitleDescriptionViewCardController.swift in Sources */,
1B1E204022F4ADE900734EDA /* CardPartRoundedStackViewCardController.swift in Sources */,
1B693BA1230DAE2900662382 /* CardPartIconLabelCardController.swift in Sources */,
70F595D020B5C56200B6E688 /* CardPartTextViewCardController.swift in Sources */,
70F595D420B5C7B300B6E688 /* CardPartButtonViewCardController.swift in Sources */,
70F595D620B5C87300B6E688 /* CardPartTitleViewCardController.swift in Sources */,
Expand Down
110 changes: 110 additions & 0 deletions Example/CardParts/CardPartIconLabelCardController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//
// CardPartIconLabelCardController.swift
// CardParts_Example
//
// Created by Venkatnarayansetty, Badarinath on 8/21/19.
// Copyright © 2019 CocoaPods. All rights reserved.
//

import Foundation
import CardParts
import RxSwift
import RxCocoa

class CardPartIconLabelCardController: CardPartsViewController {

var viewModel = ReactiveCardPartIconViewModel()

override func viewDidLoad() {

let alignment: CardPartIconLabel.VerticalPosition = .center

let stackView = CardPartStackView()
stackView.axis = .vertical
stackView.spacing = 10

for index in 0...5 {

let iconLabel = CardPartIconLabel()
iconLabel.verticalPadding = 10
iconLabel.horizontalPadding = 10
iconLabel.backgroundColor = UIColor(red: 16.0 / 255.0, green: 128.0 / 255.0, blue: 0, alpha: 0.16)
iconLabel.font = UIFont.systemFont(ofSize: 12)
iconLabel.textColor = UIColor.black
iconLabel.numberOfLines = 0
iconLabel.iconPadding = 5
iconLabel.layer.cornerRadius = 8.0
iconLabel.icon = UIImage(named: "themeIcon")

switch index {
case 0:
iconLabel.text = "Card icon on left,text is left"
iconLabel.textAlignment = .left
iconLabel.iconPosition = ( .left, alignment )

case 1:
iconLabel.text = "Card icon on right,text is left"
iconLabel.textAlignment = .left
iconLabel.iconPosition = ( .right, alignment )

case 2:
iconLabel.text = "Card icon on left,text is right"
iconLabel.textAlignment = .right
iconLabel.iconPosition = (.left, alignment)

case 3:
iconLabel.text = "Card icon on right,text is right"
iconLabel.textAlignment = .right
iconLabel.iconPosition = (.right, alignment)

case 4:
iconLabel.text = "Card icon on left,text is center"
iconLabel.textAlignment = .center
iconLabel.iconPosition = (.left, alignment)

case 5:
iconLabel.text = "Card icon on right,text is center"
iconLabel.textAlignment = .center
iconLabel.iconPosition = (.right, alignment)

default:
break
}

stackView.addArrangedSubview(iconLabel)

if index == 1 {
viewModel.labelText.asObservable().bind(to: iconLabel.rx.labelText).disposed(by: bag)
viewModel.iconView.asObservable().bind(to: iconLabel.rx.iconView).disposed(by: bag)
invalidateLayout(onChanges: [viewModel.iconView])
}
}

setupCardParts([stackView])
}
}

class ReactiveCardPartIconViewModel {

var labelText = BehaviorRelay(value: "Defaul Label")
var iconView = BehaviorRelay(value: UIImageView(image: UIImage(named: "")))

init() {
startTimer()
}

func startTimer() {
iconView.accept( UIImageView(image: UIImage(named: "cardIcon")))
Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(self.randomise), userInfo: nil, repeats: true)
}

@objc func randomise() {
switch arc4random() % 1 {
case 0:
labelText.accept("CardParts is reactive based library")
default:
return
}
}

}
3 changes: 2 additions & 1 deletion Example/CardParts/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class MainViewController: CardsViewController {
StateCardController(), // Demo states
CardPartBorderViewController(), // Border Cards
CardPartPillLabelCardController(), // Pill label
CardPartRoundedStackViewCardController() //Rounded Stackview
CardPartRoundedStackViewCardController(), //Rounded Stackview
CardPartIconLabelCardController() // Icon label
]

loadCards(cards: cards)
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ CardParts - made with ❤️ by Intuit:
- [CardPartTitleView](#cardparttitleview)
- [CardPartTitleDescriptionView](#cardparttitleview)
- [CardPartPillLabel](#cardpartpilllabel)
- [CardPartIconLabel](#cardparticonlabel)
- [CardPartSeparatorView](#cardpartseparatorview)
- [CardPartVerticalSeparatorView](#cardpartverticalseparatorview)
- [CardPartTableView](#cardparttableview)
Expand Down Expand Up @@ -507,6 +508,26 @@ var horizontalPadding:CGFloat

See the example app for a working example.

#### `CardPartIconLabel`

CardPartIconLabel provides the capability to add images in eithet directions supporting left , right and center text alignments.

```swift
let iconLabel = CardPartIconLabel()
iconLabel.verticalPadding = 10
iconLabel.horizontalPadding = 10
iconLabel.backgroundColor = UIColor.blue
iconLabel.font = UIFont.systemFont(ofSize: 12)
iconLabel.textColor = UIColor.black
iconLabel.numberOfLines = 0
iconLabel.iconPadding = 5
iconLabel.icon = UIImage(named: "cardIcon")
```

<p align="center">
<img src="https://raw.githubusercontent.com/Intuit/CardParts/master/images/cardPartIconLabel.png" width="300" alt="cardPartIconLabel"/>
</p>

#### `CardPartSeparatorView`

CardPartSeparatorView displays a separator line. There are no reactive properties define for CardPartSeparatorView.
Expand Down
Binary file added images/cardPartIconLabel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3c9fa78

Please sign in to comment.