Displaying content in DSKit is easy, all you have to do is just call one function with content you want to display or want to update.
show(content: [YOUR_SECTION])
Content is composed of two data structures DSSection and DSViewModel
DSSection - is used to describe how content will be displayed on the screen, in one word: Layout DSViewModel - is used to describe your content on the screen, labels, texts, images, actions, and so on.
You can display your view models in Lists, Grids, and Galleries.
To easily transform an array of DSViewModel's in a section just call .list() .grid() .gallery() method on your view models array.
Every time your need to update content on the screen, add, delete, change position, just change your content structure and call show(content: [YOUR_SECTION])
DSKit will automatically update the changes, remove non-existent content, or switch position, and it will be animated.
let texts = ["Petrichor","Sumptuous","Angst","Aesthete","Nadir"]
let viewModels = texts.map { (text) -> DSViewModel in
DSLabelVM(.body, text: text)
}
show(content: viewModels.list())
As for the center content of the screen you can easily show content on the bottom of the screen, all you have to do is just call
showBottom(content: [YOUR_SECTION])
everything related to the show content from the show content section is also valid here
let texts = ["Petrichor","Sumptuous","Angst","Aesthete","Nadir"]
let viewModels = texts.map { (text) -> DSViewModel in
DSLabelVM(.body, text: text)
}
showBottom(content: viewModels.list())
As for the center content of the screen you can easily show content on the top of the screen, all you have to do is just call
showTop(content: [YOUR_SECTION])
everything related to the show content from the show content section is also valid here
let texts = ["Petrichor","Sumptuous","Angst","Aesthete","Nadir"]
let viewModels = texts.map { (text) -> DSViewModel in
DSLabelVM(.body, text: text)
}
show(content: viewModels.list())
Here is an example of how to show and update content in all positions of the screen
var showBottom = DSButtonVM(title: "Show Bottom")
showBottom.didTap { [unowned self] (_ :DSButtonVM) in
self.showBottom()
}
var hideBottom = DSButtonVM(title: "Hide Bottom")
hideBottom.didTap { [unowned self] (_ :DSButtonVM) in
self.hideBottom()
}
var showTop = DSButtonVM(title: "Show Top")
showTop.didTap { [unowned self] (_ :DSButtonVM) in
self.showTop()
}
var hideTop = DSButtonVM(title: "Hide Top")
hideTop.didTap { [unowned self] (_ :DSButtonVM) in
self.hideTop()
}
var showCenter = DSButtonVM(title: "Show Center")
showCenter.didTap { [unowned self] (_ :DSButtonVM) in
self.showCenter = true
self.showDemo()
}
var hideCenter = DSButtonVM(title: "Hide Center")
hideCenter.didTap { [unowned self] (_ :DSButtonVM) in
self.showCenter = false
self.showDemo()
}
// Texts
let texts = ["Petrichor","Sumptuous","Angst","Aesthete","Nadir","Petrichor"]
let textViewModels = texts.map { (text) -> DSViewModel in
DSLabelVM(.body, text: text)
}
var sections = [DSSection]()
let buttonsSection = [showBottom,
hideBottom,
showTop,
hideTop,
showCenter,
hideCenter].grid(identifier: "TopButtons")
sections.append(buttonsSection)
let textsSection = textViewModels.list(grouped: true, identifier: "List")
sections.append(textsSection)
if self.showCenter {
let imageSection = DSImageVM(named: "barbershop", height: .absolute(300)).list(identifier: "BarbeshopImage")
sections.append(imageSection)
}
show(content: sections)
let texts = ["Petrichor","Sumptuous","Angst","Aesthete","Nadir"]
let viewModels = texts.map { (text) -> DSViewModel in
DSLabelVM(.body, text: text)
}
show(content: viewModels.list())
let texts = ["Miraculous","Lassitude","Gossamer","Bungalow","Scintilla"]
let viewModels: [DSViewModel] = texts.map { (text) -> DSLabelVM in
DSLabelVM(.body, text: text)
}
show(content: viewModels.list(separator: true))
let texts = ["Aurora","Inure","Mellifluous","Euphoria","Serendipity"]
let viewModels: [DSViewModel] = texts.map { (text) -> DSLabelVM in
DSLabelVM(.body, text: text)
}
show(content: viewModels.list(grouped: true))
let texts = ["Cherish", "Demure", "Elixir", "Eternity", "Felicity"]
let viewModels: [DSViewModel] = texts.map { (text) -> DSLabelVM in
DSLabelVM(.body, text: text)
}
show(content: viewModels.list(separator: true, grouped: true))
// Image view models
var imageViewModels = ["barbershop", "beautysaloon"].map { (name) -> DSViewModel in
var image = DSImageVM(image: UIImage(named: name), height: .absolute(100))
let label = DSLabelVM(.subheadline, text: name)
image.supplementaryItems = [label.asSupplementary(position: .leftTop)]
return image
}
// Action view models
let actionViewModels = ["flowerstore", "grocerystore"].map { (name) -> DSViewModel in
// Action
var action = DSActionVM(title: name)
action.topImage(image: UIImage(named: name), height: .equalTo(100), contentMode: .scaleAspectFill)
// Label
let label = DSLabelVM(.headlineWithSize(12), text: name.capitalized)
// Button
var button = DSButtonVM(sfSymbol: "heart.fill",
type: .blur(effect: .dark, color: .white))
button.width = .absolute(40)
button.height = .absolute(40)
// Set supplementary items
action.supplementaryItems = [label.asSupplementary(position: .leftTop, background: .lightBlur),
button.asSupplementary(position: .rightTop, background: .clear)]
return action
}
imageViewModels.append(contentsOf: actionViewModels)
show(content: imageViewModels)
// Image view models
var imageViewModels = ["barbershop", "beautysaloon"].map { (name) -> DSViewModel in
// Image view
var image = DSImageVM(image: UIImage(named: name), height: .absolute(50))
// Picker
let picker = DSQuantityPickerVM()
picker.height = .absolute(25)
picker.width = .absolute(100)
// Set picker view as right side view
image.rightSideView = DSSideView(view: picker)
return image
}
// Action view models
let actionViewModels = ["flowerstore", "grocerystore"].map { (name) -> DSViewModel in
// Action
var action = DSActionVM(title: name)
action.topImage(image: UIImage(named: name), height: .equalTo(100), contentMode: .scaleAspectFill)
// Picker
let picker = DSQuantityPickerVM()
picker.width = .absolute(100)
picker.height = .absolute(30)
picker.rightButton(title: "Add",
sfSymbolName: "cart.fill.badge.plus",
style: .custom(size: 18, weight: .medium)) {
print("Add to cart")
}
// Bottom side view
action.bottomSideView = DSSideView(view: picker)
return action
}
imageViewModels.append(contentsOf: actionViewModels)
show(content: imageViewModels)
let viewModels = [1,2,3,4,5].map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(150)
return viewModel
}
show(content: viewModels.grid())
let viewModels = [1,2,3,4,5].map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(100)
return viewModel
}
show(content: viewModels.grid(columns: 3, grouped: true))
let viewModels = ["barbershop", "beautysaloon", "flowerstore", "grocerystore"].map { (name) -> DSViewModel in
// Image
var image = DSImageVM(named: name, height: .absolute(200))
// Label
let label = DSLabelVM(.subheadline, text: name.capitalized)
let labelSupplementaryView = DSSupplementaryView(view: label, position: .leftBottom)
// Button
var button = DSButtonVM(title: "Like",
icon: UIImage(systemName: "heart.fill"),
type: .blur(effect: .dark, color: .white))
button.width = .absolute(80)
button.height = .absolute(40)
let buttonSupplementaryView = DSSupplementaryView(view: button,
position: .rightTop,
background: .clear)
image.supplementaryItems = [labelSupplementaryView, buttonSupplementaryView]
return image
}
show(content: viewModels.grid())
let viewModels = [1,2,3,4,5,6,7,8,9,10,11,12].map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(150)
viewModel.width = .absolute(150)
return viewModel
}
show(content: viewModels.gallery())
let numbers = [1,2,3,4,5,6]
let numbers2 = [7,8,9,10,11,12]
let viewModels3 = numbers.map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(150)
viewModel.width = .fractional(0.7)
return viewModel
}
let viewModels5 = numbers2.map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(100)
viewModel.width = .fractional(0.5)
return viewModel
}
show(content: [viewModels3.gallery(), viewModels5.gallery()])
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12]
let viewModels = numbers.map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(150)
viewModel.width = .fractional(1.0)
return viewModel
}
show(content: [viewModels.gallery(grouped: true)])
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12]
let viewModels = numbers.map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(200)
viewModel.width = .fractional(1.0)
return viewModel
}
show(content: viewModels.gallery())
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12]
let viewModels = numbers.map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(300)
viewModel.width = .fractional(1.0)
viewModel.imageDisplayStyle = .default
return viewModel
}
// Full width section
let section = viewModels.gallery(type: .fullWidth)
section.zeroTopInset()
show(content: section)
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12]
let viewModels = numbers.map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(250)
viewModel.width = .fractional(1.0)
return viewModel
}
let pageControl = DSPageControlVM(type: .viewModels(viewModels))
let section = pageControl.list().zeroLeftRightInset()
section.zeroLeftRightInset()
show(content: section)
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12]
let viewModels = numbers.map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(200)
viewModel.width = .fractional(0.7)
return viewModel
}
let pageControl = DSPageControlVM(type: .viewModels(viewModels))
let section = pageControl.list()
section.zeroLeftRightInset()
show(content: [section])
let viewModels = ["barbershop", "beautysaloon", "flowerstore", "grocerystore"].map { (name) -> DSViewModel in
// Image
var image = DSImageVM(named: name, height: .absolute(200))
// Label
let label = DSLabelVM(.subheadline, text: name.capitalized)
let labelSupplementaryView = DSSupplementaryView(view: label, position: .leftBottom)
// Button
var button = DSButtonVM(title: "Like",
icon: UIImage(systemName: "heart.fill"),
type: .blur(effect: .dark, color: .white))
button.width = .absolute(80)
button.height = .absolute(40)
let buttonSupplementaryView = DSSupplementaryView(view: button,
position: .rightTop,
background: .clear)
image.supplementaryItems = [labelSupplementaryView, buttonSupplementaryView]
return image
}
show(content: viewModels.gallery())
In essence, typography is the art of arranging letters and text in a way that makes the copy legible, clear, and visually appealing to the reader. Typography involves font style, appearance, and structure, which aims to elicit certain emotions and convey specific messages.
let title1 = DSLabelVM(.title1, text: "Title 1")
let title2 = DSLabelVM(.title2, text: "Title 2")
let title3 = DSLabelVM(.title3, text: "Title 3")
let headline = DSLabelVM(.headline, text: "Headline")
let subheadline = DSLabelVM(.subheadline, text: "Subheadline")
let body = DSLabelVM(.body, text: "Body")
let callout = DSLabelVM(.callout, text: "Callout")
let caption1 = DSLabelVM(.caption1, text: "Caption 1")
let caption2 = DSLabelVM(.caption2, text: "Caption 2")
let footnote = DSLabelVM(.footnote, text: "Footnote")
show(content: [title1, title2, title3, headline, subheadline, body, callout, caption1, caption2, footnote])
let text1 = DSLabelVM(.headline, text: "An astronomical term that's been coopted for colloquial usage")
let text2 = DSLabelVM(.subheadline, text: "Suffering from a lack of energy? Describe your tiredness—whether it's in your body, your mind.")
let text3 = DSLabelVM(.caption1, text: "Not to be confused with those furry crepuscular rodents, scintilla means a spark or a trace of something.")
show(content: [text1, text2, text3].list())
let text1 = DSLabelVM(.headline, text: "An astronomical term that's been coopted for colloquial usage")
let text2 = DSLabelVM(.subheadline, text: "Suffering from a lack of energy? Describe your tiredness—whether it's in your body, your mind.")
let text3 = DSLabelVM(.caption1, text: "Not to be confused with those furry crepuscular rodents, scintilla means a spark or a trace of something.")
show(content: [text1, text2, text3].grid())
let text1 = DSLabelVM(.headline, text: "An astronomical term that's been coopted for colloquial usage")
var text2 = DSLabelVM(.subheadline, text: "Suffering from a lack of energy? Describe your tiredness—whether it's in your body, your mind.")
text2.style.displayStyle = .grouped(inSection: false)
let text3 = DSLabelVM(.caption1, text: "Not to be confused with those furry crepuscular rodents, scintilla means a spark or a trace of something.")
show(content: [text1, text2, text3].gallery())
let composer = DSTextComposer()
composer.add(type: .title1, text: "Nadir")
composer.add(type: .subheadline, text: "An astronomical term that's been coopted for colloquial usage, nadir means the lowest point, as in the \"nadir of her popularity.\" Its opposite term, zenith, has a similar appeal.")
composer.add(price: DSPrice(amount: "100.0", currency: "$"))
composer.add(price: DSPrice(amount: "50.0", currency: "$"), size: .large)
composer.add(price: DSPrice(amount: "50.0", currency: "$"), size: .extraLarge)
composer.add(sfSymbol: "staroflife.circle.fill", style: .small, tint: .custom(UIColor.systemYellow))
composer.add(sfSymbol: "staroflife.circle.fill", style: .medium, tint: .custom(UIColor.systemYellow))
composer.add(sfSymbol: "staroflife.circle.fill", style: .large, tint: .custom(UIColor.systemYellow))
composer.add(sfSymbol: "moon.stars.fill", style: .custom(size: 60, weight: .light), tint: .custom(UIColor.systemYellow))
composer.add(sfSymbol: "flame.fill", style: .medium, tint: .custom(UIColor.systemRed))
composer.add(sfSymbol: "star.fill", style: .large)
composer.add(sfSymbols: ["tray.fill", "star.square", "newspaper.fill", "person.crop.circle.fill.badge.minus", "smoke.fill"], style: .large, tint: .custom(UIColor.systemTeal))
composer.add(rating: 3, maximumValue: 5, positiveSymbol: "star.fill", negativeSymbol: "star", tint: .custom(UIColor.systemYellow))
show(content: composer.textViewModel())
let nadir = DSTextComposer()
nadir.add(type: .title1, text: "Nadir")
nadir.add(type: .subheadline, text: "An astronomical term that's been coopted for colloquial usage.")
let lassitude = DSTextComposer()
lassitude.add(type: .headline, text: "Lassitude")
lassitude.add(type: .subheadline, text: "Suffering from a lack of energy? Describe your tiredness—whether it's in your body, your mind.")
var nadirAction = nadir.actionViewModel()
nadirAction.rightArrow()
var lassitudeAction = lassitude.actionViewModel()
lassitudeAction.rightImage(named: "flowerstore")
show(content: nadirAction, lassitudeAction)
var activeText = DSActiveTextVM(.body, text: "An astronomical term that's been coopted for colloquial #usage, nadir means the lowest point, as in the \"nadir of her popularity.\" Its @opposite term, noreply@dskit.com zenith, has a similar appeal.")
activeText.links = ["astronomical": "https://dskit.app"]
activeText.didTapOnHashTag = { tag in
self.show(message: "Did tap on hashtag: #\(tag)")
}
activeText.didTapOnUrl = { url in
self.show(message: "Did tap on url:\(url.absoluteString)")
}
activeText.didTapOnEmail = { email in
self.show(message: "Did tap on email: \(email)")
}
activeText.didTapOnMention = { mention in
self.show(message: "Did tap on mention: @\(mention)")
}
show(content: activeText)
// Default button style
var defaultButton = DSButtonVM(title: "Default")
defaultButton.didTap { [unowned self] (button: DSButtonVM) in
self.show(message: "Did dap on Default button", timeOut: 1)
}
// Light button style
var lightButton = DSButtonVM(title: "Light", type: .light)
lightButton.didTap { [unowned self] (button: DSButtonVM) in
self.show(message: "Did dap on light button", timeOut: 1)
}
// Link button style
var linkButton = DSButtonVM(title: "Link", type: .link)
linkButton.didTap { [unowned self] (button: DSButtonVM) in
self.show(message: "Did dap on link button")
}
// Buttons with icons
let defaultWithIcon = DSButtonVM(title: "Default", icon: UIImage(systemName: "message.fill"))
var lightButtonWithIcon = DSButtonVM(title: "Light", icon: UIImage(systemName: "figure.wave"), type: .light)
lightButtonWithIcon.imagePosition = .right
// Link with icon
let linkButtonWithIcon = DSButtonVM(title: "Link", icon: UIImage(systemName: "flame.fill"), type: .link)
// Default left
let defaultButtonLeftAlignment = DSButtonVM(title: "Default left", textAlignment: .left)
// Light right
let lightButtonRightAlignment = DSButtonVM(title: "Light right", type: .light, textAlignment: .right)
// Link left
let linkButtonLeft = DSButtonVM(title: "Link left", type: .link, textAlignment: .left)
// Link right
let linkButtonRight = DSButtonVM(title: "Link right", type: .link, textAlignment: .right)
// Default button - right image
var defaultButtonLeftWithRightImage = DSButtonVM(title: "Left text right image",
icon: UIImage(systemName: "message.fill"),
textAlignment: .center)
defaultButtonLeftWithRightImage.imagePosition = .right
// Default button - left image
var defaultButtonRightWithLeftImage = DSButtonVM(title: "Left image right text",
icon: UIImage(systemName: "message.fill"),
textAlignment: .right)
defaultButtonRightWithLeftImage.imagePosition = .left
// Default button - right margin image
var defaultButtonLeftWithRightMarginImage = DSButtonVM(title: "Left text right margin image",
icon: UIImage(systemName: "message.fill"),
textAlignment: .left)
defaultButtonLeftWithRightMarginImage.imagePosition = .rightMargin
// Default button - left margin image
var defaultButtonRightWithLeftMarginImage = DSButtonVM(title: "Left image right text",
icon: UIImage(systemName: "message.fill"),
textAlignment: .right)
defaultButtonRightWithLeftMarginImage.imagePosition = .leftMargin
show(content: defaultButton,
lightButton,
linkButton,
defaultWithIcon,
lightButtonWithIcon,
linkButtonWithIcon,
defaultButtonLeftAlignment,
lightButtonRightAlignment,
linkButtonLeft,
linkButtonRight,
defaultButtonLeftWithRightImage,
defaultButtonRightWithLeftImage,
defaultButtonLeftWithRightMarginImage,
defaultButtonRightWithLeftMarginImage)
// Texts
let texts = ["Petrichor", "Sumptuous", "Angst", "Aesthete", "Nadir"]
// Map texts into an array of view models
let viewModels = texts.map { (text) -> DSViewModel in
var button = DSButtonVM(title: text)
// Handle did tap on button
button.didTap { [unowned self] (button: DSButtonVM) in
self.show(message: text)
}
return button
}
// Show
show(content: viewModels.grid())
// Texts
let texts = ["Petrichor", "Sumptuous", "Angst", "Aesthete", "Nadir"]
// Map texts into an array of view models
let viewModels = texts.map { (text) -> DSViewModel in
var button = DSButtonVM(title: text)
// Handle button tap on button
button.didTap { [unowned self] (button: DSButtonVM) in
self.show(message: text)
}
return button
}
// Show
show(content: viewModels.list())
// Texts
let texts = ["Petrichor", "Sumptuous", "Angst", "Aesthete", "Nadir"]
// Map texts into an array of view models
let viewModels = texts.map { (text) -> DSViewModel in
var button = DSButtonVM(title: text)
// Handle button tap on button
button.didTap { [unowned self] (button: DSButtonVM) in
self.show(message: text)
}
return button
}
// Show
show(content: viewModels.gallery())
// Barber Shop
let barberShop = DSImageVM(named: "barbershop",
height: .absolute(100),
displayStyle: .circle)
// Beauty Saloon
let beautySaloon = DSImageVM(named: "beautysaloon",
height: .absolute(100),
displayStyle: .themeCornerRadius)
// Flower Saloon
let flowerStore = DSImageVM(named: "flowerstore",
height: .absolute(100),
displayStyle: .default,
contentMode: .scaleAspectFit)
// Show
show(content: barberShop, beautySaloon, flowerStore)
// Images
let imageNames = ["barbershop", "beautysaloon", "flowerstore", "grocerystore"]
// Map texts into an array of view models
let viewModels = imageNames.map { (name) -> DSViewModel in
DSImageVM(image: UIImage(named: name), height: .absolute(100))
}
// Show
show(content: viewModels.list())
// Images
let imageNames = ["barbershop", "beautysaloon", "flowerstore", "grocerystore"]
// Map texts into an array of view models
let viewModels = imageNames.map { (name) -> DSViewModel in
DSImageVM(image: UIImage(named: name), height: .absolute(100))
}
// Show
show(content: viewModels.grid())
// Images
let imageNames = ["barbershop", "beautysaloon", "flowerstore", "grocerystore"]
// Map texts into an array of view models
let viewModels = imageNames.map { (name) -> DSViewModel in
DSImageVM(image: UIImage(named: name), height: .absolute(250))
}
// Show
show(content: viewModels.gallery())
// Images
let imageNames = ["barbershop", "beautysaloon", "flowerstore", "grocerystore"]
// MARK: - Handle tap on image
// Map texts into an array of view models
let viewModels = imageNames.map { (name) -> DSViewModel in
var image = DSImageVM(image: UIImage(named: name), height: .absolute(250))
image.didTap = { (_: DSViewModel) in
self.show(message: "Did tap on image \(name)")
}
return image
}
// MARK: - Handle tap on array of images
// Map texts into an array of view models
var viewModels2 = imageNames.map { (name) -> DSViewModel in
DSImageVM(image: UIImage(named: name), height: .absolute(100))
}
viewModels2 = viewModels2.didTap { (imageViewModel: DSImageVM) in
self.show(message: "Did tap on image")
}
// Show
show(content: viewModels.gallery(), viewModels2.list())
Display a list of text fields
// Text fields
let fullName = DSTextFieldVM(placeholder: "Full Name")
let email = DSTextFieldVM(placeholder: "Email")
let password = DSTextFieldVM(placeholder: "Password")
// Show
show(content: fullName, email, password)
Display a list of text fields with initial valid values
// Full name
let fullName = DSTextFieldVM.name(text: "Borinschi Ivan",placeholder: "Full Name")
fullName.leftSFSymbolName = "person.crop.circle.fill"
// Email
let email = DSTextFieldVM.email(text: "imodeveloperlab@gmail.com", placeholder: "Email")
email.leftSFSymbolName = "envelope.fill"
// Password
let password = DSTextFieldVM.password(text: "qqqqqqqq",placeholder: "Password")
password.leftSFSymbolName = "lock.shield.fill"
// Section
let section = [fullName, email, password].list()
// Show
show(content: section)
Display a list of text fields with initial invalid values
// Full name
let fullName = DSTextFieldVM.name(text: "12345",placeholder: "Full Name")
fullName.leftSFSymbolName = "person.crop.circle.fill"
// Email
let email = DSTextFieldVM.email(text: "Ivan", placeholder: "Email")
email.leftSFSymbolName = "envelope.fill"
// Password
let password = DSTextFieldVM.password(text: "qqq",placeholder: "Password")
password.leftSFSymbolName = "lock.shield.fill"
// Section
let section = [fullName, email, password].list()
// Header
section.headlineHeader("User form")
// Footer
section.subheadlineFooter("Form description")
// Show
show(content: section)
If built-in text field validation is not enough for your project requirements you can easily add custom validation to any text field in your form. Using handleValidation closure you are in control, if you implement handleValidation closure, all built-in validations will not affect the validation, you are in control for minimum or the maximum number of characters and other validation patterns.
// Custom text field for full name
let fullName = DSTextFieldVM(text: "Borinschi Ivan", placeholder: "Full Name")
// Handle text field validation
fullName.handleValidation = { textFieldText in
// Check if text is not nil
guard let text = textFieldText else {
return false
}
if text.contains("123") {
// Set textfield as invalid state
return false
} else {
// Set textfield as valid state
return false
}
}
// Handle textfield update, this closure is called every time
// user type a character in textfield
fullName.didUpdate = { textField in
print(textField.text ?? "No value")
}
// Show
show(content: fullName)
Customize your error message displayed directly in the text field if the user types an invalid value, you can add a message or an example of what kind of value he should type.
// Full name
let fullName = DSTextFieldVM.name(text: "12345",placeholder: "Full Name")
fullName.errorPlaceHolderText = "Full name should not contain numbers"
// Email
let email = DSTextFieldVM.email(text: "Ivan", placeholder: "Email")
email.errorPlaceHolderText = "Email example@example.com"
// Password
let password = DSTextFieldVM.password(text: "qqq",placeholder: "Password")
email.errorPlaceHolderText = "Minimum length should be 8"
// Section
let section = [fullName, email, password].list()
// Show
show(content: section)
DSKit contains built-in validation, you can easily add any validation you need for your form using these three properties:
Regular expressions snippet:
let patternNumbers = "^[0-9]*$"
let patternLetters = "^[a-zA-Z]*$"
let patternNameUmlaute = "^[\\ü\\ö\\ä\\Ä\\Ü\\Ö\\ß\\u0600-\\u06FFa-zA-Z\\s\\'\\-]*$"
let patternName = "^[\\u0600-\\u06FFa-zA-Z\\s\\'\\-]*$"
let patternLettersAndSpaces = "^[\\u0600-\\u06FFa-zA-Z\\s]*$"
let patternLettersAndNumbers = "^[a-zA-Z0-9]*$"
let patternLettersNumbersAndSpaces = "^[\\u0600-\\u06FFa-zA-Z0-9\\s]*$"
let patternAddressUmlaute = "^[\\ü\\ö\\ä\\Ä\\Ü\\Ö\\ß\\u0600-\\u06FFa-zA-Z0-9\\s\\'\\-]*$"
let patternAddress = "^[\\u0600-\\u06FFa-zA-Z0-9\\s\\'\\-]*$"
let patternEmail = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let patternPhoneNumber = "^\\s*(?:\\+?(\\d{1,3}))?([-. (]*(\\d{3})[-. )]*)?((\\d{3})[-. ]*(\\d{2,4})(?:[-.x ]*(\\d+))?)\\s*$"
let patternPhoneNumber2 = "(\\+?( |-|\\.)?\\d{1,3}( |-|\\.)?)?(\\(?\\d{1,5}\\)?|\\d{1,5})( |-|\\.)?(\\d{1,4}( |-|\\.)?\\d{3,4})"
let patternMin8Max20CharsAtLeastOneDigitAtLeastOneLetter = "^(?=.*\\d)((?=.*[a-z])|(?=.*[A-Z])).*$"
let patternCityNameUmlaute = "^[\\ü\\ö\\ä\\Ä\\Ü\\Ö\\ß\\u0600-\\u06FFa-zA-Z\\s\\'\\-]*$"
let patternCityName = "^[\\u0600-\\u06FFa-zA-Z\\s\\'\\-]*$"
// Custom text field for full name
let fullName = DSTextFieldVM(text: "Borinschi Ivan", placeholder: "Full Name")
// Minimum 4 characters for textfield to be valid
fullName.validateMinimumLength = 4
// Maximum 50 characters for textfield to be valid
fullName.validateMaximumLength = 50
// Validation regular expression
fullName.validationPattern = "^[a-zA-Z]*$"
// Handle textfield update, this closure is called every time user type a character in textfield
fullName.didUpdate = { textField in
print(textField.text)
}
// Show
show(content: fullName)
DSKit contains built-in text field configurations, just pick the one you need and add it to your form, each configuration comes with a specific regular expression validation, icon, minimum and maximum number of required characters for validation.
We can use a shortcut text field and add some custom elements after initialization, for example changing the minimum of characters for a text-filed to be valid.
// Email
let email = DSTextFieldVM.email(placeholder: "Email")
email.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Phone
let phone = DSTextFieldVM.phone(placeholder: "Phone")
phone.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Secure Code
let secureCode = DSTextFieldVM.secureCode(placeholder: "Secure Code")
secureCode.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Password
let password = DSTextFieldVM.password(placeholder: "Password")
password.didUpdate = { tf in
print(tf.text ?? "No value")
}
// New Password
let newPassword = DSTextFieldVM.newPassword(placeholder: "New Password")
newPassword.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Name
let name = DSTextFieldVM.name(placeholder: "Name")
name.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Given Name
let givenName = DSTextFieldVM.givenName(placeholder: "Given Name")
givenName.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Family Name
let familyName = DSTextFieldVM.familyName(placeholder: "Family Name")
familyName.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Number
let number = DSTextFieldVM.number(placeholder: "Number")
number.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Address
let address = DSTextFieldVM.address(placeholder: "Address")
number.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Street Address Line 1
let streetAddressLine1 = DSTextFieldVM.streetAddressLine1(placeholder: "Street Address Line 1")
streetAddressLine1.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Street Address Line 2
let streetAddressLine2 = DSTextFieldVM.streetAddressLine2(placeholder: "Street Address Line 2")
streetAddressLine2.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Address State
let addressState = DSTextFieldVM.addressState(placeholder: "Address State")
addressState.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Address City
let addressCity = DSTextFieldVM.addressCity(placeholder: "Address City")
addressCity.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Address City And State
let addressCityAndState = DSTextFieldVM.addressCityAndState(placeholder: "Address City And State")
addressCityAndState.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Search
let search = DSTextFieldVM.search(placeholder: "Search")
search.didUpdate = { tf in
print(tf.text ?? "No value")
}
// Show
show(content: [email,
phone,
secureCode,
password,
newPassword,
name,
givenName,
familyName,
number,
address,
streetAddressLine1,
streetAddressLine2,
addressState,
addressCity,
addressCityAndState,
search])
Display a list of text fields grouped in section
// Text fields
let fullName = DSTextFieldVM(placeholder: "Full Name")
let email = DSTextFieldVM(placeholder: "Email")
let password = DSTextFieldVM(placeholder: "Password")
// Text fields section
let section = [fullName, email, password].list(grouped: true)
// Header
section.headlineHeader("User form")
// Footer
section.subheadlineFooter("Form description")
// Show
show(content: section)
Customize your text fields with specific icons using apple SF Symbols library https://developer.apple.com/sf-symbols/
// Full name
let fullName = DSTextFieldVM.name(placeholder: "Full Name")
// Set left sf symbol https://developer.apple.com/sf-symbols/
fullName.leftSFSymbolName = "person.crop.circle.fill"
// Email
let email = DSTextFieldVM.email(placeholder: "Email")
// Set left sf symbol https://developer.apple.com/sf-symbols/
email.leftSFSymbolName = "envelope.fill"
// Password
let password = DSTextFieldVM.password(placeholder: "Password")
// Set left sf symbol https://developer.apple.com/sf-symbols/
password.leftSFSymbolName = "lock.shield.fill"
// Section
let section = [fullName, email, password].list(grouped: true)
// Header
section.headlineHeader("User form")
// Footer
section.subheadlineFooter("Form description")
// Show
show(content: section)
Display a list of text fields in a grid layout
// Text fields
let fullName = DSTextFieldVM(placeholder: "Full Name")
let email = DSTextFieldVM(placeholder: "Email")
let password = DSTextFieldVM(placeholder: "Password")
// Show
show(content: [fullName, email, password].grid())
Display a list of text fields in a grouped grid layout
// Text fields
let fullName = DSTextFieldVM(placeholder: "Full Name")
let email = DSTextFieldVM(placeholder: "Email")
let password = DSTextFieldVM(placeholder: "Password")
// Show
show(content: [fullName, email, password].grid(grouped: true))
// Data
let data = ["Petrichor": "leaf.fill", "Sumptuous": "safari.fill", "Angst": "paintbrush.pointed.fill"]
let sortedData = data.sorted(by: { $0 < $1 })
// Map data into an array of actions
let actions = sortedData.map { text -> DSViewModel in
var action = DSActionVM(title: text.key)
action.leftIcon(sfSymbolName: text.value)
action.height = .absolute(30)
return action
}
// Show content
show(content: actions.list(grouped: true), actions.list(separator: true, grouped: true))
// Texts
let texts = ["Petrichor", "Sumptuous", "Angst", "Aesthete", "Nadir"]
// Map texts into an array of view models
let viewModels = texts.map { (text) -> DSViewModel in
DSActionVM(title: text)
}
// Show
show(content: viewModels.list())
// Texts
let texts = ["Petrichor", "Sumptuous", "Angst", "Aesthete", "Nadir"]
// Map texts into an array of view models
let viewModels = texts.map { (text) -> DSViewModel in
DSActionVM(title: text)
}
// Show
show(content: viewModels.grid())
// Texts
let texts = ["Petrichor", "Sumptuous", "Angst", "Aesthete", "Nadir"]
// Map texts into an array of view models
let viewModels = texts.map { (text) -> DSViewModel in
DSActionVM(title: text)
}
// Show
show(content: viewModels.gallery())
// Data
let data = ["Petrichor": "leaf.fill", "Sumptuous": "safari.fill", "Angst": "paintbrush.pointed.fill"]
let sortedData = data.sorted(by: { $0 < $1 })
// Map data into an array of view models
let actions = sortedData.map { text -> DSViewModel in
var action = DSActionVM(title: text.key)
action.leftIcon(sfSymbolName: text.value)
return action
}
// Show
show(content: actions)
// Barber shop
let barbershop = DSTextComposer()
barbershop.add(type: .headline, text: "Barber Shop")
barbershop.add(type: .subheadline, text: "Subheadline text")
var barbershopAction = DSActionVM(composer: barbershop)
// Set top image
barbershopAction.topImage(image: UIImage(named: "barbershop"), height: .equalTo(50))
// Beauty saloon
var beautySaloonAction = DSActionVM(title: "Beauty Saloon")
// Set top image
beautySaloonAction.topImage(image: UIImage(named: "beautysaloon"), height: .unknown)
beautySaloonAction.height = .absolute(150)
// Beauty saloon
var flowerStoreAction = DSActionVM(title: "Flower Store", subtitle: "Best flower store in town")
// Set top image
flowerStoreAction.topImage(image: UIImage(named: "flowerstore"), height: .equalTo(200))
// Right button
flowerStoreAction.rightButton(title: "More details", sfSymbolName: "leaf.fill", style: .small) { [unowned self] in
self.show(message: "Did tap on more details")
}
// Show
show(content: barbershopAction, beautySaloonAction, flowerStoreAction)
// Data
let data = ["Barbershop": "barbershop", "Beauty Saloon": "beautysaloon", "Flower Store": "flowerstore"]
let sortedData = data.sorted(by: { $0 < $1 })
// Map data into an array of view models
let actions = sortedData.map { text -> DSViewModel in
// Action
var action = DSActionVM(title: text.key)
let image = UIImage(named: text.value)
// Set left round image
action.leftRoundImage(image: image)
return action
}
// Show
show(content: actions)
// Data
let data = ["Barbershop": "barbershop", "Beauty Saloon": "beautysaloon", "Flower Store": "flowerstore"]
let sortedData = data.sorted(by: { $0 < $1 })
// Default image size
var widthHeight = 40
// Map data into an array of view models
let actions = sortedData.map { text -> DSViewModel in
// Action with title and subtitle
var action = DSActionVM(title: text.key, subtitle: "Image name: \(text.value)")
// Image
let image = UIImage(named: text.value)
// Set left round image
action.leftRoundImage(image: image, size: CGSize(width: widthHeight, height: widthHeight))
// Increase image size for each view model
widthHeight += 15
return action
}
// Show
show(content: actions)
// Barbershop
var barberShop = DSActionVM(title: "Barbershop", subtitle: "themeCornerRadius, scaleAspectFill")
// Left image
barberShop.leftImage(image: UIImage(named: "barbershop"),
style: .themeCornerRadius,
size: .size(CGSize(width: 50, height: 50)),
contentMode: .scaleAspectFill)
// Beauty Saloon
var beautysaloon = DSActionVM(title: "Beauty Saloon", subtitle: "circle, scaleAspectFill")
// Left image
beautysaloon.leftImage(image: UIImage(named: "beautysaloon"),
style: .circle,
size: .size(CGSize(width: 30, height: 30)),
contentMode: .scaleAspectFill)
// Flowers Store
var flowerstore = DSActionVM(title: "Flower Store", subtitle: "default, scaleAspectFit")
// Left image
flowerstore.leftImage(image: UIImage(named: "flowerstore"),
style: .default,
size: .size(CGSize(width: 70, height: 70)),
contentMode: .scaleAspectFit)
// Show
show(content: barberShop, beautysaloon, flowerstore)
// Data
let data = ["Barbershop": "barbershop", "Beauty Saloon": "beautysaloon", "Flower Store": "flowerstore"]
let sortedData = data.sorted(by: { $0 < $1 })
// Map data into an array of view models
let actions = sortedData.map { text -> DSViewModel in
// Action
var action = DSActionVM(title: text.key)
let image = UIImage(named: text.value)
// Set right round image
action.rightRoundImage(image: image)
return action
}
// Show
show(content: actions)
// Data
let data = ["Petrichor": "leaf.fill", "Sumptuous": "safari.fill", "Angst": "paintbrush.pointed.fill"]
let sortedData = data.sorted(by: { $0 < $1 })
// Map data into an array of view models
let actions = sortedData.map { text -> DSViewModel in
// Action
var action = DSActionVM(title: text.key)
// Right icon
action.rightIcon(sfSymbolName: text.value)
return action
}
// Show
show(content: actions)
// Data
let data = ["Barbershop": "barbershop", "Beauty Saloon": "beautysaloon", "Flower Store": "flowerstore"]
let sortedData = data.sorted(by: { $0 < $1 })
// Default image size
var widthHeight = 40
// Map data into an array of view models
let actions = sortedData.map { text -> DSViewModel in
// Action with title and subtitle
var action = DSActionVM(title: text.key, subtitle: "Image name: \(text.value)")
// Image
let image = UIImage(named: text.value)
// Set right round image
action.rightRoundImage(image: image, size: CGSize(width: widthHeight, height: widthHeight))
// Increase image size for each view model
widthHeight += 15
return action
}
// Show
show(content: actions)
// Barber Shop
var barberShop = DSActionVM(title: "Barbershop", subtitle: "themeCornerRadius, scaleAspectFill")
// Right image
barberShop.rightImage(image: UIImage(named: "barbershop"),
style: .themeCornerRadius,
size: .size(CGSize(width: 50, height: 50)),
contentMode: .scaleAspectFill)
// Barber Saloon
var beautySaloon = DSActionVM(title: "Beauty Saloon", subtitle: "circle, scaleAspectFill")
// Right image
beautySaloon.rightImage(image: UIImage(named: "beautysaloon"),
style: .circle,
size: .size(CGSize(width: 30, height: 30)),
contentMode: .scaleAspectFill)
// Flower Store
var flowerStore = DSActionVM(title: "Flower Store", subtitle: "default, scaleAspectFit")
// Right image
flowerStore.rightImage(image: UIImage(named: "flowerstore"),
style: .default,
size: .size(CGSize(width: 70, height: 70)),
contentMode: .scaleAspectFit)
// Show
show(content: barberShop, beautySaloon, flowerStore)
// Action 1
var action = DSActionVM(title: "Action", subtitle: "Button")
action.rightButton(sfSymbolName: "cart.fill.badge.plus") { [unowned self] in
self.show(message: "Did tap on button")
}
// Action 2
var action2 = DSActionVM(title: "Action", subtitle: "Small button")
action2.rightButton(title: "Small", sfSymbolName: "trash.fill", style: .small) { [unowned self] in
self.show(message: "Did tap on small button")
}
// Action 3
var action3 = DSActionVM(title: "Action", subtitle: "Medium button")
action3.rightButton(title: "Medium", sfSymbolName: "trash.fill", style: .medium) { [unowned self] in
self.show(message: "Did tap on medium button")
}
// Action 4
var action4 = DSActionVM(title: "Action", subtitle: "Large button")
action4.rightButton(title: "Large", sfSymbolName: "trash.fill", style: .large) { [unowned self] in
self.show(message: "Did tap on large button")
}
// Show
show(content: action, action2, action3, action4)
// Image
let image = DSImageVM(named: "glasses", height: .absolute(250))
// Compose text
let composer = DSTextComposer()
let spacing = appearance.interItemSpacing
composer.add(type: .headlineWithSize(40), text: "Discover your best fashion glasses", spacing: spacing, maximumLineHeight: 38)
composer.add(type: .subheadline, text: "Get all brands at one place. Fill the bag\nwith full joy", spacing: spacing)
composer.add(type: .headline, text: "Only ")
composer.add(price: DSPrice(amount: "99.0", regularAmount: "129.00", currency: "$"), size: .extraLarge, newLine: false)
// Space
let space = DSSpaceVM()
// Page with view models
let page = DSPageVM(viewModels: [image, space, composer.textViewModel()])
var pageControl = DSPageControlVM(type: .pages([page]))
pageControl.height = .absolute(UIDevice.current.contentAreaHeigh - 80)
// Show page
self.show(content: pageControl)
// Text composer
let text = DSTextComposer()
text.add(type: .headline, text: "325 Broadway, Bayonne, NJ 07002")
text.add(type: .subheadline, text: "15 minutes to drive")
// Location
let location = CLLocationCoordinate2D(latitude: 40.764382, longitude: -73.973045)
// Map
let map = DSMapVM(text: text, coordinate: location)
// Show
show(content: map)
// Text composer
let composer = DSTextComposer(alignment: .left)
composer.add(type: .text(font: .headlineWithSize(18), color: .white), text: "John Doe")
composer.add(type: .text(font: .headlineWithSize(24), color: .white), text: "0006 5236 1689 9521")
composer.add(type: .text(font: .subheadline, color: .white), text: "Exp: 24/23")
// Credit Card
let creditCard = DSCardVM(composer: composer,
textPosition: .bottom,
backgroundImage: .image(image: UIImage(named: "visa-card-bg")))
// Text composer 2
let composer2 = DSTextComposer(alignment: .right)
composer2.add(type: .text(font: .headlineWithSize(18), color: .white), text: "Bonus Card")
composer2.add(type: .text(font: .headlineWithSize(24), color: .white), text: "0006 5236 1689 9521")
composer2.add(type: .text(font: .subheadline, color: .white), text: "Discount: 10%, Points 0, Cash Back 5%")
composer2.add(price: DSPrice(amount: "50", currency: "$", discountBadge: "BONUS"), size: .large)
// Bonus Card
var bonusCard = DSCardVM(composer: composer2, textPosition: .bottom,
backgroundImage: .imageURL(url: URL(string: "https://images.pexels.com/photos/291762/pexels-photo-291762.jpeg?cs=srgb&dl=pexels-freestocksorg-291762.jpg&fm=jpg")))
// Bonus Card gradient
bonusCard.gradientTopColor = UIColor.black.withAlphaComponent(0.1)
bonusCard.gradientBottomColor = DSColor.color(light: UIColor.black.withAlphaComponent(1.0), dark: UIColor.black.withAlphaComponent(0.7))
// Show
show(content: creditCard, bonusCard)
// Segments
let segments = ["Petrichor", "Sumptuous", "Angst"]
// Segment controll
let segmentControl = DSSegmentVM(segments: ["Petrichor","Sumptuous","Angst"])
// Handle tap on segment
segmentControl.didTapOnSegment = { segment in
let text = DSLabelVM(.body, text: segment.title)
// Update
self.show(content: segmentControl, text)
}
// Text
let text = DSLabelVM(.body, text: segments.first ?? "No value")
// Show
show(content: segmentControl, text)
// Numbers
let numbers = [1, 2, 3, 4]
// Map numbers into an array of view models
let viewModels = numbers.map { (index) -> DSViewModel in
var viewModel = DSImageVM(image: UIImage(named: "picture-\(index)"))
viewModel.height = .absolute(200)
return viewModel
}
// Page controll
let pageControl = DSPageControlVM(type: .viewModels(viewModels))
// Sections
let section = pageControl.list().zeroLeftRightInset()
let section2 = pageControl.list().zeroLeftRightInset()
// Show
show(content: section, section2)
Main DSKit idea is to configure the app appearance once and to not care anymore about spaces colors and typography, if you need just that please read the article about appearance in DSKit
But DSKit offers more than that, you can add small adjustments to specific view models in your UI if you need that.
Using style property on your view model you can adjust borderStyle
, colorStyle
, cornerStyle
,shadowStyle
Explore the example bellow:
// Button view model with custom colors
var button = DSButtonVM(title: "Button")
// Get a copy current appearance colors from secondaryView colors
var colors = DSAppearance.shared.main.secondaryView
// Change colors
colors.button.background = UIColor.green
colors.button.title = UIColor.blue
// Set color style to the button
button.style.colorStyle = .custom(colors)
// Image View Model with custom border
var imageViewModel = DSImageVM(named: "barbershop", height: .absolute(100), displayStyle: .themeCornerRadius)
imageViewModel.style.borderStyle = .custom(width: 5, color: .blue)
// Action with custom corner radius
var action = DSActionVM(title: "Action")
action.style.cornerStyle = .custom(3)
// Action with default display style
var action2 = DSActionVM(title: "Action")
action2.style.displayStyle = .default
// Action with custom shadow
var action3 = DSActionVM(title: "Action")
action3.style.shadowStyle = .small
show(content: [button, imageViewModel, action, action2, action3].list())