inspired by Esat GÖZCÜ CustomTextField 🙇🏻♂️
Cassette is an Custom View library written in SwiftUI.
- Requirements
- iOS 13.0+
- Installation
- features
.package(url: "https://github.com/ios-carki/Cassette.git", from: "v1.1.10")
I will guide you on how to use BtnCassette(Button) in SwiftUI.
- Basic
struct TestView: View {
var body: some View {
VStack(spacing: 20) {
// Normal Button
BtnCassette(buttonMode: .normal(text: "Normal Button"))
// Image Button - Left Positioned Image
BtnCassette(buttonMode: .imageButton(text: "Leading Image Button", imageDirection: .leading, imageType: .system, imageName: "globe"))
// Image Button - Right Positioned Image
BtnCassette(buttonMode: .imageButton(text: "Trailing Image Button", imageDirection: .trailing, imageType: .system, imageName: "person"))
// Binding Text Button
BtnCassette(buttonMode: .bindingText(text: .constant("Binding Text Button")))
}.padding(.horizontal, 16)
}
}
- Customizing
struct TestView: View {
@StateObject private var viewModel = TestViewModel()
var body: some View {
VStack(spacing: 20) {
BtnCassette(buttonMode: .normal(text: "Normal Button"))
.setTitleTextColor(color: .blue)
.setTitleTextFont(font: .title2)
.setCornerRadius(20)
.setBorderWidth(width: 2)
.setBorderColor(color: .blue)
.setButtonHeight(height: 80)
.setBackgroundColor(color: .cyan)
.setDisableBackgroundColor(color: .gray)
.setDisable(disable: $viewModel.buttonDisable)
.click {
viewModel.buttonDisable.toggle()
}
}.padding(.horizontal, 16)
}
}
If you set default value, you don't need to set customizing function every views
- For Storyboard Interface Project
import UIKit
import Cassette
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Here is important!
let shared = BtnCassetteConfig.shared
shared.defaultButtonTextColor = .blue
shared.defaultButtonTextFont = .title2
shared.defaultButtonCornerRadius = 20
shared.defaultBorderWidth = 2
shared.defaultBorderColor = .blue
shared.defaultButtonHeight = 80
shared.defaultButtonBackgroundColor = .cyan
shared.defaultbuttonDisableBackgroundColor = .gray
return true
}
.
.
.
}
- For SwiftUI Interface Project
import SwiftUI
import Cassette
@main
struct MyProjectApp: App {
// Here is important!
init() {
let shared = BtnCassetteConfig.shared
shared.defaultButtonTextColor = .blue
shared.defaultButtonTextFont = .title2
shared.defaultButtonCornerRadius = 20
shared.defaultBorderWidth = 2
shared.defaultBorderColor = .blue
shared.defaultButtonHeight = 80
shared.defaultButtonBackgroundColor = .cyan
shared.defaultbuttonDisableBackgroundColor = .gray
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
- With Using Default Variable
import SwiftUI
import Cassette
struct TestView: View {
@StateObject private var viewModel = TestViewModel()
var body: some View {
VStack(spacing: 20) {
BtnCassette(buttonMode: .normal(text: "Normal Button"))
.setDisable(disable: $viewModel.buttonDisable)
.click {
viewModel.buttonDisable.toggle()
}
}.padding(.horizontal, 16)
}
}
The result is the same as above, but the code is much shorter.
- Basic
![스크린샷 2023-09-12 오후 5 26 51](https://private-user-images.githubusercontent.com/44957712/267264311-a66f74e5-ef57-4360-b57b-75c54915340a.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjAzNTQ1MTIsIm5iZiI6MTcyMDM1NDIxMiwicGF0aCI6Ii80NDk1NzcxMi8yNjcyNjQzMTEtYTY2Zjc0ZTUtZWY1Ny00MzYwLWI1N2ItNzVjNTQ5MTUzNDBhLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzA3VDEyMTAxMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTAzYWE2MmY3NDE2OTEyNWRkNWZhOTIyN2NlNGY5YjFkMDk4M2VmMTQ3MWM0MzAxMzM4MzFhM2QxZGQ5ZTExMDAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.TxolqPKZovgoZRJyhdDd01o984sLEEowJ24iekQ8aq8)
//normal
SwitchCassette(switchType: .normal(text: Binding<String>?, isOn: Binding<Bool>, spacing: CGFloat?))
//rectangle
SwitchCassette(switchType: .rectangle(text: Binding<String>?, isOn: Binding<Bool>, spacing: CGFloat?))
- Customizing
import SwiftUI
import Cassette
struct ContentView: View {
@StateObject private var viewModel = ContentViewModeel()
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
SwitchCassette(text: $viewModel.text, isOn: $viewModel.isOn, spacing: 12)
.setSwitchControllerColor(color: .blue)
.setTitleTextOffColor(color: .blue)
.setTitleTextOnColor(color: .red)
.setSwitchOnBackgroundColor(color: .indigo)
.insertSpacer(true)
}
}
.padding(.horizontal, 16)
}
}
![스크린샷 2023-09-08 오후 2 44 24](https://private-user-images.githubusercontent.com/44957712/266513191-701470e8-ee56-4f7e-917c-c5dc991dd832.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjAzNTQ1MTIsIm5iZiI6MTcyMDM1NDIxMiwicGF0aCI6Ii80NDk1NzcxMi8yNjY1MTMxOTEtNzAxNDcwZTgtZWU1Ni00ZjdlLTkxN2MtYzVkYzk5MWRkODMyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzA3VDEyMTAxMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTMwNTlhMmMyZWIyOTg1YmVkMTg4MjliMmYxMGI3NDFhMGU2NmQwMTljNWZhMjQxYWE2N2Q1ZjBiZDFkOWFkZTcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.kykASl0MFcpWG4Yn-QMQYC0B4Han0cwhMDf6Zdr787I)
import SwiftUI
import Cassette
struct ContentView: View {
@StateObject private var viewModel = ContentViewModeel()
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
SwitchCassette(text: $viewModel.text, isOn: $viewModel.isOn, spacing: 12)
.setSwitchControllerColor(color: .blue)
.setTitleTextOffColor(color: .blue)
.setTitleTextOnColor(color: .red)
.setSwitchOnBackgroundColor(color: .indigo)
.insertSpacer(false) // here is changed!
}
}
.padding(.horizontal, 16)
}
}
![스크린샷 2023-09-08 오후 2 45 46](https://private-user-images.githubusercontent.com/44957712/266513396-8b458160-25f2-47a4-9db2-7cc4f434746d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjAzNTQ1MTIsIm5iZiI6MTcyMDM1NDIxMiwicGF0aCI6Ii80NDk1NzcxMi8yNjY1MTMzOTYtOGI0NTgxNjAtMjVmMi00N2E0LTlkYjItN2NjNGY0MzQ3NDZkLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzA3VDEyMTAxMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTA5NGY0NDFhYjQwNTY2MmEwMGI5Zjk2MzI4ZTM5NDg3Nzc4MzgxYTAwNjc0YzU0YWJmYzc3MTQ4OTg3MGRhNWEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.IGb1yJgRIvD7NF1dTTblhLWBbVKwPe5eJdOHyn41B40)
import SwiftUI
import Cassette
struct ContentView: View {
@StateObject private var viewModel = ContentViewModeel()
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
SwitchCassette(text: $viewModel.text, isOn: $viewModel.isOn, spacing: 100) // spacing value changed!
.setSwitchControllerColor(color: .blue)
.setTitleTextOffColor(color: .blue)
.setTitleTextOnColor(color: .red)
.setSwitchOnBackgroundColor(color: .indigo)
.insertSpacer(false)
}
}
.padding(.horizontal, 16)
}
}
![스크린샷 2023-09-08 오후 2 46 57](https://private-user-images.githubusercontent.com/44957712/266513601-b396f564-3a2e-41f6-967a-fb3f1dd1df9d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjAzNTQ1MTIsIm5iZiI6MTcyMDM1NDIxMiwicGF0aCI6Ii80NDk1NzcxMi8yNjY1MTM2MDEtYjM5NmY1NjQtM2EyZS00MWY2LTk2N2EtZmIzZjFkZDFkZjlkLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzA3VDEyMTAxMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWUyNWU4NTU2OWVmNmM5OTRkNGYxODJjNzU0MGVjNmQzYWY2ZWRjZDgwMzI5MWE3ZDAwMjZiYjk4NGIwNjA0ZGMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.1h7qIzyL9HmoulEzS3VpRLx75WWb-pra1b8VbcsX6Sk)
If you set default value, you don't need to set customizing function every views
- For Storyboard Interface Project
import UIKit
import Cassette
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Here is important!
let shared = SwitchCassetteConfig.shared
shared.defaultSwitchOffTextColor = .blue
shared.defaultSwitchOnTextColor = .red
shared.defaultSwitchTextFont = .callout
shared.defaultSwitchOnBackgroundColor = .indigo
shared.defaultSwitchOffBackgroundColor = .gray
shared.defaultSwitchControllerColor = .blue
return true
}
.
.
.
}
- For SwiftUI Interface Project
import SwiftUI
import Cassette
@main
struct MyProjectApp: App {
// Here is important!
init() {
let shared = SwitchCassetteConfig.shared
shared.defaultSwitchOffTextColor = .blue
shared.defaultSwitchOnTextColor = .red
shared.defaultSwitchTextFont = .callout
shared.defaultSwitchOnBackgroundColor = .indigo
shared.defaultSwitchOffBackgroundColor = .gray
shared.defaultSwitchControllerColor = .blue
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
- With Using Default Variable
import SwiftUI
import Cassette
struct ContentView: View {
@StateObject private var viewModel = ContentViewModeel()
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
SwitchCassette(text: $viewModel.text, isOn: $viewModel.isOn, spacing: 100)
.insertSpacer(false)
}
}
.padding(.horizontal, 16)
}
}
The result is the same as above, but the code is much shorter.
![스크린샷 2023-09-08 오후 2 54 53](https://private-user-images.githubusercontent.com/44957712/266514984-f85eec65-d4c8-4013-a173-a4d0187d20da.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjAzNTQ1MTIsIm5iZiI6MTcyMDM1NDIxMiwicGF0aCI6Ii80NDk1NzcxMi8yNjY1MTQ5ODQtZjg1ZWVjNjUtZDRjOC00MDEzLWExNzMtYTRkMDE4N2QyMGRhLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzA3VDEyMTAxMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWIzZjgwNjc2MTAzYzUyOWJjNDM2YWQ1NDAwYzg5OWE1ODlhNzZiZTBmZjcxZDhmNzVmNjBmY2FkODFkYzhmNWImWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.pyQWgwQtUqVyz2mplTQKI8qXyi3Nj4txloMI8mL1SXo)
- Basic
TextFieldCassette(mode: .underLine(placeHolder: "asdfok", text: $viewModel.text, title: "Title", alignment: .leading))
- Customizing
//Customizing
TextFieldCassette(mode: .underLine(placeHolder: "asdfok", text: $viewModel.text, title: "Title", alignment: .leading))
.setReactangleFieldBackgroundColor(.white) // SetBackgroundColor
.setPlaceHolderTextColor(.blue) // SetPlaceHolderTextColor
.setRectangleFieldBorderColor(.black) //SetBorderColor
.setImageButton(imageDirection: .trailing, imageType: .system(color: .black), imageName: "person") {
print("Hello") // SetImageButton
}
- Result
- SetError
struct ContentView: View {
@StateObject private var viewModel = ContentViewModeel()
var body: some View {
VStack(alignment: .leading, spacing: 8) {
VStack {
TextFieldCassette(mode: .underLine(placeHolder: "asdfok", text: $viewModel.text, title: "Title", alignment: .leading))
.setSecureField(isSecure: false)
.setError(isError: $viewModel.isError, errorColor: .red)
}
}
.padding(.horizontal, 16)
}
}
final class ContentViewModeel: ObservableObject {
@Published var isOn: Bool = false
@Published var text: String = "" { didSet { setError()}}
@Published var isError: Bool = false
func setError() {
if text == "abc" {
isError = true
} else {
isError = false
}
}
}
- Error Result
Simulator.Screen.Recording.-.iPhone.14.Pro.Max.-.2023-09-18.at.12.40.08.mp4
If you set default value, you don't need to set customizing function every views
//Title
public var defaultTitleTextFont: Font = .callout
public var defaultTitleTextColor: Color = .black
public var defaultTitleTextSpacing: CGFloat = 8
public var defaultTitleTextAlignment: Alignment = .leading
//Text
public var defaultTextFont: Font = .callout
public var defaultTextColor: Color = .black
public var defaultTextAlignment: Alignment = .leading
//PlaceHolder
public var defaultPlaceHolderTextColor: Color = .gray
public var defaultPlaceHolderTextFont: Font = .callout
public var defaultPlaceHolderTextAlignment: Alignment = .leading
//Design
public var defaultTextFieldHeight: CGFloat = 50
public var defaultRectangleFieldBackgroundColor: Color = .yellow
public var defaultRectangleFieldCornerRadius: CGFloat = 12
public var defaultRectangleFieldBorderColor: Color = .orange
public var defaultRectangleFieldBorderWidth: CGFloat = 2
//Error
public var defaultErrorColor: Color = .red
//SecureField
setSecureField(isSecure: Bool)
//Title
setTitleTextFont(_ font: Font)
setTitleTextColor(_ color: Color)
setTitleTextSpacing(spacing: CGFloat)
setTitleTextAlignment(alignment: Alignment)
//Text
setTextFont(_ font: Font)
setTextColor(_ color: Color)
setTextAlignment(alignment: Alignment)
//PlaceHolder
setPlaceHolderTextColor(_ color: Color)
setPlaceHolderTextFont(_ font: Font)
setPlaceHolderTextAlignment(alignment: Alignment)
//Design
setTextFieldHeight(_ height: CGFloat)
setReactangleFieldBackgroundColor(_ color: Color)
setRectangleFieldCornerRadius(_ radius: CGFloat)
setRectangleFieldBorderColor(_ color: Color)
setRectangleFieldBorderWidth(_ width: CGFloat)
//Error
setError(isError: Binding<Bool>, errorColor: Color)
//Image
setImageButton(imageDirection: ImageDirection, imageType: ImageType, imageName: String, action: (() -> ())?)