Using CocoaPods
Edit your Podfile
and specify the dependency:
pod 'MLBusinessComponents'
import MLBusinessComponents
Choose and instantiate your component.
Each component is a subclass of UIView.
This component allow you to show the progress ring of points, a label and actionable button. The most common use of this component is to show a user's progress within the loyalty program.
You need to set MLBusinessLoyaltyRingData
protocol (interfase). This protocol allow you to populate the draw data into component. (Ring progress percent, ring color, label text, button title and button deeplink).
let ringView = MLBusinessLoyaltyRingView(_ ringViewData: MLBusinessLoyaltyRingData)
view.addSubView(ringView)
/*
Set your constraints. You don't need to set up the HEIGHT contraint.
Because this component is responsible for knowing and setting your own HEIGHT.
*/
NSLayoutConstraint.activate([
ringView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
ringView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
ringView.topAnchor.constraint(equalTo: view.topAnchor)
])
This protocol allow you to providade the proper data to draw MLBusinessLoyaltyRingView
. You can setup ring progress percent, ring color, label text, button title and button deeplink). Each value is mandatory.
@objc public protocol MLBusinessLoyaltyRingData: NSObjectProtocol {
@objc func getRingHexaColor() -> String
@objc func getRingNumber() -> Int
@objc func getRingPercentage() -> Float
@objc func getTitle() -> String
@objc func getButtonTitle() -> String
@objc func getButtonDeepLink() -> String
}
Implementation of MLBusinessLoyaltyRingData
example:
class MyDrawDataForRingView: NSObject, MLBusinessLoyaltyRingData {
func getRingNumber() -> Int {
return 3
}
func getRingHexaColor() -> String {
return "#17aad6"
}
func getRingPercentage() -> Float {
return 0.80
}
func getTitle() -> String {
return "Ganaste 100 Mercado Puntos"
}
func getButtonTitle() -> String {
return "Mis beneficios"
}
func getButtonDeepLink() -> String {
return "mercadopago://beneficios"
}
}
You can be informed when the user presses the button of the component and receive the deeplink previously sent in MLBusinessLoyaltyRingData
. Just add tapAction callback.
let ringView = MLBusinessLoyaltyRingView(MyDrawDataForRingView())
view.addSubView(ringView)
// Add tap action and receive the deepLink
ringView.addTapAction { deepLink in
print(deepLink)
}
This component allow you to show a group of N items in a grid system (3 cols by default). You can add a title and subtitle for the main component. Also, you can provide imageUrl, title and subtitle for each item. This component is responsible for knowing and setting your own height based on number of cols and item quantity.
You need to set MLBusinessDiscountBoxData
protocol. This interface allow you to populate the draw data into component. (Title, subtitle for the main component and imageUrl. Title, subtitle, deepLinkItem and trackId for each item).
// DiscountData() is an implementation of MLBusinessDiscountBoxData protocol.
let discountView = MLBusinessDiscountBoxView(DiscountData())
view.addSubview(discountView)
/*
Set your constraints. You don't need to set up the HEIGHT contraint.
Because this component is responsible for knowing and setting your own HEIGHT.
*/
NSLayoutConstraint.activate([
discountView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
discountView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
discountView.topAnchor.constraint(equalTo: targetView.bottomAnchor)
])
This protocol allow you to providade the proper data to draw MLBusinessDiscountBoxView
. You can setup title and subtitle for the main component and a list of MLBusinessDiscountSingleItemProtocol
that represent each element of the cell.
@objc public protocol MLBusinessDiscountBoxData: NSObjectProtocol {
@objc optional func getTitle() -> String?
@objc optional func getSubtitle() -> String?
@objc func getItems() -> [MLBusinessSingleItemProtocol]
}
Implementation of MLBusinessDiscountBoxData
in DiscountData example:
class DiscountData: NSObject, MLBusinessDiscountBoxData {
func getTitle() -> String {
return "200 descuentos"
}
func getSubtitle() -> String {
return "por ser nivel 3"
}
func getItems() -> [MLBusinessSingleItemProtocol] {
var array: [MLBusinessSingleItemProtocol] = [MLBusinessSingleItemProtocol]()
array.append(SingleItemData(title: "Hasta", subtitle: "$ 200", iconImageUrl: "https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/McDonald%27s_Golden_Arches.svg/1200px-McDonald%27s_Golden_Arches.svg.png"))
array.append(SingleItemData(title: "Hasta", subtitle: "$ 200", iconImageUrl: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRY0eFECzyTCa83gOV3smCYDOSIggdUxSPirwtt5rS3LcWlzefo"))
array.append(SingleItemData(title: "Hasta", subtitle: "$ 200", iconImageUrl: "https://upload.wikimedia.org/wikipedia/commons/b/b3/Logo-freddo.jpg"))
array.append(SingleItemData(title: "Hasta", subtitle: "$ 200", iconImageUrl: "https://urbancomunicacion.com/wp-content/uploads/2017/04/Logotipos-famosos-Starbucks-Urban-comunicacion-1.png"))
array.append(SingleItemData(title: "Hasta", subtitle: "$ 200", iconImageUrl: "https://www.stickpng.com/assets/images/5a1c3211f65d84088faf13e8.png"))
array.append(SingleItemData(title: "Hasta", subtitle: "$ 200", iconImageUrl: "https://pbs.twimg.com/profile_images/1124417403566395394/9Wuzg8pf.png"))
return array
}
}
This protocol/interfase represents the element of each cell for MLBusinessDiscountBoxView
.
Each element contains imageUrl, title, subtitle, deepLinkItem and trackId.
@objc public protocol MLBusinessSingleItemProtocol: NSObjectProtocol {
@objc func titleForItem() -> String
@objc func subtitleForItem() -> String
@objc func iconImageUrlForItem() -> String
@objc func deepLinkForItem() -> String?
@objc func trackIdForItem() -> String?
}
Implementation of MLBusinessSingleItemProtocol
in example:
class SingleItemData: NSObject {
let title: String
let subTitle: String
let iconUrl: String
let deepLink: String?
let trackId: String?
init(title: String, subtitle: String, iconImageUrl: String, deepLink: String? = nil, trackId: String? = nil) {
self.title = title
self.subTitle = subtitle
self.iconUrl = iconImageUrl
self.deepLink = deepLink
self.trackId = trackId
}
}
extension SingleItemData: MLBusinessSingleItemProtocol {
func titleForItem() -> String {
return title
}
func subtitleForItem() -> String {
return subTitle
}
func iconImageUrlForItem() -> String {
return iconUrl
}
func deepLinkForItem() -> String? {
return deepLink
}
func trackIdForItem() -> String? {
return trackId
}
}
You can be informed when the user presses the item of the component and receive the deeplink, trackId and item index previously sent in MLBusinessSingleItemProtocol
discountView.addTapAction { (selectedIndex, deepLink, trackId) in
print(selectedIndex)
}
In order to keep the same reference and update only the data and layout you can call to update method. MLBusinessDiscountBoxView
discountView.update(_ MLBusinessDiscountBoxData)
This component allows you to draw a dividing line in order to separate views. For example, it can be used to separate the MLBusinessLoyaltyRingView component from the MLBusinessDownloadAppView component.
The init method receives an optional parameter named: 'hasTriangle'. This parameter is 'false' by default. When this parameter is sent as 'true', a rect line with a downward triangle in the center is drawn. Conversely, just a single rect line is shown.
let dividingLineView = MLBusinessDividingLineView(hasTriangle: true)
view.addSubview(dividingLineView)
/*
Set your constraints. You don't have to set up the HEIGHT contraint.
*/
NSLayoutConstraint.activate([
dividingLineView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
dividingLineView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
dividingLineView.topAnchor.constraint(equalTo: view.topAnchor)
])
This component allows you to show a view with an image of the app icon (ML or MP), a title and an actionable button in order to download the app.
You need to set MLBusinessDownloadAppData
protocol. This interface allows you to populate the draw data into the component (appSite, title, buttonTitle and buttonDeepLink, being all of them mandatory).
// DownloadAppData() is an implementation of MLBusinessDownloadAppData protocol.
let downloadAppView = MLBusinessDownloadAppView(DownloadAppData())
view.addSubview(downloadAppView)
/*
Set your constraints. You don't need to set up the HEIGHT contraint.
Because this component is responsible for knowing and setting its own HEIGHT.
*/
NSLayoutConstraint.activate([
downloadAppView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
downloadAppView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
downloadAppView.topAnchor.constraint(equalTo: targetView.bottomAnchor, constant: 16)
])
This protocol allows you to provide the proper data to draw MLBusinessDownloadAppView
. You have to setup the following data: appSite(ML or MP), title, buttonTitle, and buttonDeepLink.
@objc public protocol MLBusinessDownloadAppData: NSObjectProtocol {
@objc func getAppSite() -> MLBusinessDownloadAppView.AppSite
@objc func getTitle() -> String
@objc func getButtonTitle() -> String
@objc func getButtonDeepLink() -> String
}
Implementation of MLBusinessDownloadAppData
in DownloadAppData example:
class DownloadAppData: NSObject, MLBusinessDownloadAppData {
func getAppSite() -> MLBusinessDownloadAppView.AppSite {
return MLBusinessDownloadAppView.AppSite.MP
}
func getTitle() -> String {
return "Exclusivo con la app de Mercado Pago"
}
func getButtonTitle() -> String {
return "Descargar"
}
func getButtonDeepLink() -> String {
return "http://mercadopago"
}
}
You can be informed when the user presses the download button and receive the deeplink previously sent in MLBusinessDownloadAppData
downloadAppView.addTapAction { (deepLink) in
print(deepLink)
}
Using setBackgroundColor
method.
downloadAppView.setBackgroundColor(.red)
Using setCornerRadius
method.
downloadAppView.setCornerRadius(0)
This component allows you to show a view with an image icon, a text and an actionable button.
You need to set MLBusinessCrossSellingBoxData
protocol. This interface allows you to populate the draw data into the component (iconUrl, text, buttonTitle and buttonDeepLink, being all of them mandatory).
// CrossSellingBoxData() is an implementation of MLBusinessCrossSellingBoxData protocol.
let crossSellingBoxView = MLBusinessCrossSellingBoxView(CrossSellingBoxData())
view.addSubview(crossSellingBoxView)
/*
Set your constraints. You don't need to set up the HEIGHT contraint.
Because this component is responsible for knowing and setting its own HEIGHT.
*/
NSLayoutConstraint.activate([
crossSellingBoxView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
crossSellingBoxView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
crossSellingBoxView.topAnchor.constraint(equalTo: targetView.bottomAnchor, constant: 16)
])
This protocol allows you to provide the proper data to draw MLBusinessCrossSellingBoxView
. You have to setup the following data: iconUrl, text, buttonTitle and buttonDeepLink.
@objc public protocol MLBusinessCrossSellingBoxData: NSObjectProtocol {
@objc func getIconUrl() -> String
@objc func getText() -> String
@objc func getButtonTitle() -> String
@objc func getButtonDeepLink() -> String
}
Implementation of MLBusinessCrossSellingBoxData
in CrossSellingBoxData example:
class CrossSellingBoxData: NSObject, MLBusinessCrossSellingBoxData {
func getIconUrl() -> String {
return "https://urbancomunicacion.com/wp-content/uploads/2017/04/Logotipos-famosos-Starbucks-Urban-comunicacion-1.png"
}
func getText() -> String {
return "Ganá $ 50 de regalo para tus pagos diarios"
}
func getButtonTitle() -> String {
return "Invita a más amigos a usar la app"
}
func getButtonDeepLink() -> String {
return "https://mercadopago-crossSelling"
}
}
You can be informed when the user presses the actionable button and receive the deeplink previously sent in MLBusinessCrossSellingBoxData
crossSellingBoxView.addTapAction { (deepLink) in
print(deepLink)
}
This component allow you to show the progress ring of points, title and subtitle.
// LoyaltyHeaderData() is an implementation of MLBusinessLoyaltyHeaderData protocol.
let loyaltyHeaderView = MLBusinessLoyaltyHeaderView(LoyaltyHeaderData(), fillPercentProgress: true)
containerView.addSubview(loyaltyHeaderView)
/*
Set your constraints. You don't need to set up the HEIGHT contraint.
Because this component is responsible for knowing and setting its own HEIGHT.
*/
NSLayoutConstraint.activate([
loyaltyHeaderView.topAnchor.constraint(equalTo: targetView.bottomAnchor, constant: 16),
loyaltyHeaderView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
loyaltyHeaderView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16)
])
return loyaltyHeaderView
])
This protocol allows you to provide the proper data to draw MLBusinessLoyaltyHeaderView
. You have to setup the following data:
@objc public protocol MLBusinessLoyaltyHeaderData: NSObjectProtocol {
@objc func getBackgroundHexaColor() -> String
@objc func getPrimaryHexaColor() -> String
@objc func getSecondaryHexaColor() -> String
@objc func getRingNumber() -> Int
@objc func getRingPercentage() -> Float
@objc func getTitle() -> String
@objc func getSubtitle() -> String
}
Implementation of MLBusinessLoyaltyHeaderData
in LoyaltyHeaderData example:
import MLBusinessComponents
class LoyaltyHeaderData: NSObject, MLBusinessLoyaltyHeaderData {
func getBackgroundHexaColor() -> String {
return "1AC2B0"
}
func getPrimaryHexaColor() -> String {
return "FFFFFF"
}
func getSecondaryHexaColor() -> String {
return "65A69E"
}
func getRingNumber() -> Int {
return 4
}
func getRingPercentage() -> Float {
return 0.8
}
func getTitle() -> String {
return "Beneficios"
}
func getSubtitle() -> String {
return "Nivel 4 - Mercado Puntos"
}
}
This component allow you to show the an icon and a title.
// ItemDescriptionData() is an implementation of MLBusinessItemDescriptionData protocol.
let itemDescriptionView = MLBusinessItemDescriptionView(ItemDescriptionData())
containerView.addSubview(itemDescriptionView)
/*
Set your constraints. You don't need to set up the HEIGHT contraint.
Because this component is responsible for knowing and setting its own HEIGHT.
*/
NSLayoutConstraint.activate([
itemDescriptionView.topAnchor.constraint(equalTo: targetView.bottomAnchor, constant: 16),
itemDescriptionView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
itemDescriptionView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16)
])
This protocol allows you to provide the proper data to draw MLBusinessItemDescriptionView
. You have to setup the following data:
@objc func getTitle() -> String
@objc func getIconImageURL() -> String
@objc func getIconHexaColor() -> String
Implementation of MLBusinessItemDescriptionData
in ItemDescriptionData example:
import MLBusinessComponents
final class ItemDescriptionData: NSObject, MLBusinessItemDescriptionData {
func getTitle() -> String {
return "Envíos gratis desde $ 1.999"
}
func getIconImageURL() -> String {
return "https://http2.mlstatic.com/static/org-img/loyalty/benefits/mobile/ic-shipping-discount-64.png"
}
func getIconHexaColor() -> String {
return "1AC2B0"
}
}
This component allow you to show a animated button. When you press it, it becomes a circle and then expands across the whole screen.
/*
To implement this component we only need to initialize it with a String for its normal state
and another for its load state.
*/
let animatedButton = MLBusinessAnimatedButton(normalLabel: "Normal", loadingLabel: "Loading")
containerView.addSubview(animatedButton)
/*
Set your constraints. You don't need to set up the HEIGHT contraint.
Because this component is responsible for knowing and setting its own HEIGHT.
*/
NSLayoutConstraint.activate([
animatedButton.topAnchor.constraint(equalTo: targetView.bottomAnchor, constant: 16),
animatedButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
animatedButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16)
])
// To start the animation we must call the method called startLoading().
animatedButton.startLoading()
/*
To end the animation we must call the method called finishLoading(color: UIColor, image: UIImage)
Where we can set the color with which it will expand
and an optional image that will be displayed on the button when it becomes circular.
*/
animatedButton.finishLoading(color: .green, image: nil)
/*
Additionally, the component provides us with a function to push the next view controller with a Fade effect.
*/
animatedButton.goToNextViewController(newViewController, navigationController)
This protocol allows you to execute actions once the animation is over and also in the case of a possible time out.
@objc func didFinishAnimation(_ animatedButton: MLBusinessAnimatedButton)
@objc func progressButtonAnimationTimeOut()
@objc optional func expandAnimationInProgress()
Implementation of MLBusinessAnimatedButtonDelegate
:
import MLBusinessComponents
extension ViewController: MLBusinessAnimatedButtonDelegate {
func didFinishAnimation(_ animatedButton: MLBusinessAnimatedButton) {
guard let navigationController = navigationController else { return }
let newVC = UIViewController()
newVC.view.backgroundColor = .red
animatedButton.goToNextViewController(newVC, navigationController)
}
func progressButtonAnimationTimeOut() {
print("TimeOut")
}
func expandAnimationInProgress() {
navigationController?.setNavigationBarHidden(true, animated: true)
}
}
We use MLUI
open source library to customize accent colors and font labels. In order to change those values check the documentation of MLUI
stylesheet protocol.
https://github.com/mercadolibre/fury_mobile-ios-ui
- Bitrise for releases.
- Codebeat integration.
- Snapshot Test cases.
- SwiftLint.
- Migration to Swift 5.
- iOS13 Dark Mode variant.
- Swift package manager support.
- SwiftUI bridges / UIKit <-> SwiftUI.
- iOS 10.0+
- Swift 4.2
- xCode 9.2+
- @Objc full compatibility
This project include a Swift example project using MLBusinessComponents
basic components.
- Feel free to contribute or send feedback. Fork this project and propose your own fixes, suggestions and open a pull request with the changes.
- Juan Sanzone / [email protected]
- Esteban Boffa / [email protected]
MIT License
Copyright (c) 2019 - Mercado Pago / Mercado Libre
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.