diff --git a/CardParts/src/Classes/Card Parts/CardPartIconLabel.swift b/CardParts/src/Classes/Card Parts/CardPartIconLabel.swift new file mode 100644 index 00000000..f93501e7 --- /dev/null +++ b/CardParts/src/Classes/Card Parts/CardPartIconLabel.swift @@ -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{ + return Binder(self.base) { (label, labelText) -> () in + label.text = labelText + } + } + + public var verticalPadding: Binder { + return Binder(self.base) { (label, verticalPadding) -> () in + label.verticalPadding = verticalPadding + } + } + + public var horizontalPadding: Binder { + return Binder(self.base) { (label, horizontalPadding) -> () in + label.horizontalPadding = horizontalPadding + } + } + + public var iconView: Binder { + return Binder(self.base) { (imageView, iconView) -> () in + imageView.iconView = iconView + } + } +} diff --git a/Example/CardParts.xcodeproj/project.pbxproj b/Example/CardParts.xcodeproj/project.pbxproj index ab0cd74e..8de89b26 100644 --- a/Example/CardParts.xcodeproj/project.pbxproj +++ b/Example/CardParts.xcodeproj/project.pbxproj @@ -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 */; }; @@ -60,6 +61,7 @@ /* Begin PBXFileReference section */ 1B1E203C22F47A8700734EDA /* CardPartPillLabelCardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPartPillLabelCardController.swift; sourceTree = ""; }; 1B1E203F22F4ADE900734EDA /* CardPartRoundedStackViewCardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPartRoundedStackViewCardController.swift; sourceTree = ""; }; + 1B693BA0230DAE2900662382 /* CardPartIconLabelCardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPartIconLabelCardController.swift; sourceTree = ""; }; 1BD1073F22F22823000163D2 /* CardPartBorderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPartBorderViewController.swift; sourceTree = ""; }; 2BA2B86E3C2946F91C18390D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 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 = ""; }; @@ -237,6 +239,7 @@ 1BD1073F22F22823000163D2 /* CardPartBorderViewController.swift */, 1B1E203C22F47A8700734EDA /* CardPartPillLabelCardController.swift */, 1B1E203F22F4ADE900734EDA /* CardPartRoundedStackViewCardController.swift */, + 1B693BA0230DAE2900662382 /* CardPartIconLabelCardController.swift */, ); name = "Type of CardParts"; sourceTree = ""; @@ -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 */, diff --git a/Example/CardParts/CardPartIconLabelCardController.swift b/Example/CardParts/CardPartIconLabelCardController.swift new file mode 100644 index 00000000..a7b85702 --- /dev/null +++ b/Example/CardParts/CardPartIconLabelCardController.swift @@ -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 + } + } + +} diff --git a/Example/CardParts/MainViewController.swift b/Example/CardParts/MainViewController.swift index 6969736d..405ba5d5 100644 --- a/Example/CardParts/MainViewController.swift +++ b/Example/CardParts/MainViewController.swift @@ -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) diff --git a/README.md b/README.md index 55dfc437..7ea39751 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ CardParts - made with ❤️ by Intuit: - [CardPartTitleView](#cardparttitleview) - [CardPartTitleDescriptionView](#cardparttitleview) - [CardPartPillLabel](#cardpartpilllabel) + - [CardPartIconLabel](#cardparticonlabel) - [CardPartSeparatorView](#cardpartseparatorview) - [CardPartVerticalSeparatorView](#cardpartverticalseparatorview) - [CardPartTableView](#cardparttableview) @@ -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") +``` + +

+cardPartIconLabel +

+ #### `CardPartSeparatorView` CardPartSeparatorView displays a separator line. There are no reactive properties define for CardPartSeparatorView. diff --git a/images/cardPartIconLabel.png b/images/cardPartIconLabel.png new file mode 100644 index 00000000..cc9dc241 Binary files /dev/null and b/images/cardPartIconLabel.png differ