├── screenshot.png ├── TableForm.xcodeproj ├── xcuserdata │ ├── gazollajunior.xcuserdatad │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── TableForm.xcscheme │ └── gazolla.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ ├── gazolla.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ └── gazollajunior.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── TableForm ├── FormView.swift ├── Info.plist ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── TableViewController.swift ├── MyFormView.swift ├── AppDelegate.swift ├── FormViewController.swift └── FormCells.swift ├── TableFormTests ├── Info.plist └── TableFormTests.swift ├── TableFormUITests ├── Info.plist └── TableFormUITests.swift └── README.md /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gazolla/TableForm/HEAD/screenshot.png -------------------------------------------------------------------------------- /TableForm.xcodeproj/xcuserdata/gazollajunior.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TableForm.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TableForm.xcodeproj/project.xcworkspace/xcuserdata/gazolla.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gazolla/TableForm/HEAD/TableForm.xcodeproj/project.xcworkspace/xcuserdata/gazolla.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TableForm.xcodeproj/project.xcworkspace/xcuserdata/gazollajunior.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gazolla/TableForm/HEAD/TableForm.xcodeproj/project.xcworkspace/xcuserdata/gazollajunior.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TableForm.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TableForm/FormView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormView.swift 3 | // Form 4 | // 5 | // Created by Gazolla on 10/03/17. 6 | // Copyright © 2017 Gazolla. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FormView: UITableView { 12 | 13 | var completion: (() -> Void)? 14 | 15 | override func layoutSubviews() { 16 | super.layoutSubviews() 17 | self.completion?() 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /TableForm.xcodeproj/xcuserdata/gazolla.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TableForm.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /TableFormTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /TableFormUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /TableForm.xcodeproj/xcuserdata/gazollajunior.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TableForm.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 08177D1B1D46A3B000228592 16 | 17 | primary 18 | 19 | 20 | 08177D2A1D46A3B000228592 21 | 22 | primary 23 | 24 | 25 | 08177D351D46A3B000228592 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /TableFormTests/TableFormTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableFormTests.swift 3 | // TableFormTests 4 | // 5 | // Created by Gazolla on 25/07/16. 6 | // Copyright © 2016 Gazolla. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import TableForm 11 | 12 | class TableFormTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /TableFormUITests/TableFormUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableFormUITests.swift 3 | // TableFormUITests 4 | // 5 | // Created by Gazolla on 25/07/16. 6 | // Copyright © 2016 Gazolla. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class TableFormUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TableForm 2 | Programmatically use of TableView to create data entry forms for iOS 3 | 4 | ![](https://github.com/gazolla/TableForm/raw/master/screenshot.png) 5 | 6 | ## Supported Platforms 7 | 8 | - iOS 11 9 | - swift 4 10 | 11 | ## Installing 12 | 13 | In order to install, you'll need to copy the `FormViewController`, `FormView` and `FormCells` files into your Xcode project. 14 | 15 | ## Usage 16 | 17 | ### create Fields: 18 | 19 | ```swift 20 | let name = Field(name:"name", title:"Nome:", cellType: NameCell.self) 21 | let birth = Field(name:"birthday", title:"Nascimento:", cellType: DateCell.self) 22 | let address = Field(name:"address", title:"Address:", cellType: TextCell.self) 23 | ``` 24 | 25 | ### add Fields to Section: 26 | 27 | ```swift 28 | let sectionPersonal = [name, address, birth] 29 | ``` 30 | 31 | ### group all Sections: 32 | 33 | ```swift 34 | let sections = [sectionPersonal, sectionProfessional, sectionButton] 35 | ``` 36 | 37 | 38 | ### call ConfigureTable: 39 | 40 | ```swift 41 | let config = ConfigureForm(items: sections) 42 | ``` 43 | 44 | ### Set the configuration to TableViewController: 45 | ```swift 46 | let main = FormViewController(config: config) 47 | ``` 48 | 49 | ### Contact 50 | 51 | * Sebastian Gazolla Jr 52 | * [@gazollajr](http://twitter.com/gazollajr) 53 | * [http://about.me/gazolla](http://about.me/gazolla) 54 | 55 | ## License 56 | 57 | `TableForm` is licensed under the MIT license. 58 | -------------------------------------------------------------------------------- /TableForm/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /TableForm/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /TableForm/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /TableForm/TableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.swift 3 | // GenericTable2 4 | // 5 | // Created by Sebastiao Gazolla Costa Junior on 04/08/16. 6 | // Copyright © 2016 Sebastiao Gazolla Costa Junior. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct ConfigureTable{ 12 | var items:[Item] 13 | var cellType:AnyClass 14 | var configureCell:(_ cell:UITableViewCell, _ item:Item, _ indexPath:IndexPath)->() 15 | var selectedRow:(_ controller:TableViewController, _ indexPath:IndexPath)->() 16 | } 17 | 18 | class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 19 | 20 | var items:[Item] { 21 | didSet{ 22 | self.tableView.reloadData() 23 | } 24 | } 25 | 26 | var error:Error?{ 27 | didSet{ 28 | print(error!) 29 | } 30 | } 31 | 32 | var cellType:AnyClass 33 | var configureCell:((_ cell:UITableViewCell, _ item:Item, _ indexPath:IndexPath)->())? 34 | var selectedRow:((_ controller:TableViewController, _ indexPath:IndexPath)->())? 35 | var deselectedRow:((_ controller:TableViewController, _ indexPath:IndexPath)->())? 36 | var selected:IndexPath? 37 | 38 | init(items:[Item], cellType:AnyClass){ 39 | self.items = items 40 | self.cellType = cellType 41 | super.init(nibName: nil, bundle: nil) 42 | } 43 | 44 | init(config:ConfigureTable){ 45 | self.items = config.items 46 | self.cellType = config.cellType 47 | self.configureCell = config.configureCell 48 | self.selectedRow = config.selectedRow 49 | super.init(nibName: nil, bundle: nil) 50 | } 51 | 52 | required public init?(coder aDecoder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | 56 | lazy var tableView:UITableView = { 57 | let tv = UITableView(frame: self.view.bounds, style: .grouped) 58 | tv.autoresizingMask = [.flexibleWidth, .flexibleHeight] 59 | tv.delegate = self 60 | tv.dataSource = self 61 | tv.register(self.cellType, forCellReuseIdentifier: "cell") 62 | return tv 63 | }() 64 | 65 | override func viewDidLoad() { 66 | super.viewDidLoad() 67 | self.view.addSubview(self.tableView) 68 | } 69 | 70 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 71 | return self.items.count 72 | } 73 | 74 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 75 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) //as! SummaryCell 76 | let item = self.items[indexPath.item] 77 | configureCell?(cell, item, indexPath) 78 | cell.selectionStyle = UITableViewCellSelectionStyle.none 79 | 80 | return cell 81 | } 82 | 83 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 84 | selectedRow?(self, indexPath) 85 | } 86 | 87 | func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 88 | deselectedRow?(self, indexPath) 89 | } 90 | 91 | 92 | } 93 | -------------------------------------------------------------------------------- /TableForm.xcodeproj/xcuserdata/gazollajunior.xcuserdatad/xcschemes/TableForm.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /TableForm/MyFormView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyFormView.swift 3 | // TableForm 4 | // 5 | // Created by Gazolla on 05/10/17. 6 | // Copyright © 2017 Gazolla. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MyFormViewController: FormViewController { 12 | 13 | var gender:String? { 14 | didSet{ 15 | if let cell = self.sections?[2][0], let gender = self.gender { 16 | (cell as! LinkCell).valueLabel.text = gender 17 | } 18 | } 19 | } 20 | 21 | func createFieldsAndSections()->[[Field]]{ 22 | let name = Field(name:"name", title:"Name:", cellType: NameCell.self) 23 | let birth = Field(name:"birthday", title:"Birthday:", cellType: DateCell.self) 24 | let address = Field(name:"address", title:"Address:", cellType: TextCell.self) 25 | let sectionPersonal = [name, address, birth] 26 | let company = Field(name:"company", title:"Company:", cellType: TextCell.self) 27 | let position = Field(name:"position", title:"Position:", cellType: TextCell.self) 28 | let salary = Field(name:"salary", title:"Salary:", cellType: NumberCell.self) 29 | let sectionProfessional = [company, position, salary] 30 | let gender = Field(name: "gender", title:"Gender:", cellType: LinkCell.self) 31 | let sectionGender = [gender] 32 | return [sectionPersonal, sectionProfessional, sectionGender] 33 | } 34 | 35 | lazy var saveButton: UIBarButtonItem = { 36 | return UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveTapped)) 37 | }() 38 | 39 | lazy var genderList = { ()-> TableViewController in 40 | let genders = ["male", "female"] 41 | let genderList = TableViewController(items:genders, cellType: UITableViewCell.self) 42 | 43 | genderList.configureCell = { (cell, item, indexPath) in 44 | cell.textLabel?.text = "\(item)" 45 | } 46 | 47 | genderList.selectedRow = { (controller, indexPath) in 48 | if let cell = controller.tableView.cellForRow(at: indexPath as IndexPath){ 49 | cell.accessoryType = .checkmark 50 | controller.selected = indexPath 51 | self.gender = cell.textLabel?.text 52 | } 53 | controller.navigationController?.popViewController(animated: true) 54 | } 55 | 56 | genderList.deselectedRow = { (controller, indexPath) in 57 | if controller.selected != nil { 58 | if let cell = controller.tableView.cellForRow(at: controller.selected!){ 59 | cell.accessoryType = .none 60 | } 61 | } 62 | } 63 | 64 | genderList.title = "Venues" 65 | 66 | return genderList 67 | }() 68 | 69 | override init(){ 70 | super.init() 71 | let its = createFieldsAndSections() 72 | self.fields = its 73 | self.sections = buildCells(items: its) 74 | self.selectedRow = { [weak self] (form:FormViewController,indexPath:IndexPath) in 75 | let cell = form.tableView.cellForRow(at: indexPath) 76 | cell?.isSelected = false 77 | if cell is LinkCell { 78 | if (cell as! FormCell).name == "gender" { 79 | self?.navigationController?.pushViewController(self!.genderList, animated: true) 80 | } 81 | } 82 | } 83 | } 84 | 85 | override init(config:ConfigureForm){ 86 | super.init(config:config) 87 | } 88 | 89 | required init?(coder aDecoder: NSCoder) { 90 | fatalError("init(coder:) has not been implemented") 91 | } 92 | 93 | override func viewDidLoad() { 94 | super.viewDidLoad() 95 | title = "Employee" 96 | navigationItem.rightBarButtonItem = saveButton 97 | navigationController?.navigationBar.prefersLargeTitles = true 98 | } 99 | 100 | override func viewDidAppear(_ animated: Bool) { 101 | super.viewDidAppear(animated) 102 | } 103 | 104 | @objc func saveTapped(){ 105 | let dic = self.getFormData() 106 | let alertController = UIAlertController(title: "Form Data", message: dic.description, preferredStyle: .alert) 107 | //We add buttons to the alert controller by creating UIAlertActions: 108 | let actionOk = UIAlertAction(title: "OK", 109 | style: .default, 110 | handler: nil) //You can use a block here to handle a press on this button 111 | 112 | alertController.addAction(actionOk) 113 | self.present(alertController, animated: true, completion: nil) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /TableForm/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TableForm 4 | // 5 | // Created by Gazolla on 25/07/16. 6 | // Copyright © 2016 Gazolla. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | var nav: UINavigationController? 17 | 18 | func createFieldsAndSections()->[[Field]]{ 19 | let name = Field(name:"name", title:"Nome:", cellType: NameCell.self) 20 | let birth = Field(name:"birthday", title:"Nascimento:", cellType: DateCell.self) 21 | let address = Field(name:"address", title:"Address:", cellType: TextCell.self) 22 | let sectionPersonal = [name, address, birth] 23 | let company = Field(name:"company", title:"Company:", cellType: TextCell.self) 24 | let position = Field(name:"position", title:"Position:", cellType: TextCell.self) 25 | let salary = Field(name:"salary", title:"Salary:", cellType: NumberCell.self) 26 | let sectionProfessional = [company, position, salary] 27 | let slider = Field(name: "test", title:"test:", cellType: SliderCell.self) 28 | let sectionSlider = [slider] 29 | let swap = Field(name: "cool", title:"Is it cool?", cellType: SwitchCell.self) 30 | let sectionSwap = [swap] 31 | let stepper = Field(name: "count", title:"Count:", cellType: StepperCell.self) 32 | let sectionStepper = [stepper] 33 | let save = Field(name: "Save", title:"Save", cellType: ButtonCell.self) 34 | let sectionButton = [save] 35 | return [sectionPersonal, sectionProfessional, sectionSlider, sectionSwap, sectionStepper, sectionButton] 36 | } 37 | 38 | func createConfigureTableStruct(formFields:[[Field]])->ConfigureForm{ 39 | 40 | return ConfigureForm(fields: formFields, selectedRow: { (form, indexPath) in 41 | let cell = form.tableView.cellForRow(at: indexPath as IndexPath) 42 | if cell is ButtonCell { 43 | cell?.isSelected = false 44 | let dic = form.getFormData() 45 | let alertController = UIAlertController(title: "Form Data", message: dic.description, preferredStyle: .alert) 46 | //We add buttons to the alert controller by creating UIAlertActions: 47 | let actionOk = UIAlertAction(title: "OK", 48 | style: .default, 49 | handler: nil) //You can use a block here to handle a press on this button 50 | 51 | alertController.addAction(actionOk) 52 | 53 | self.nav?.present(alertController, animated: true, completion: nil) 54 | } 55 | }) 56 | 57 | } 58 | 59 | func createForm()->FormViewController { 60 | let formFields = self.createFieldsAndSections() 61 | let config = self.createConfigureTableStruct(formFields: formFields) 62 | let myForm = FormViewController(config:config) 63 | myForm.title = "Form" 64 | let f = DateFormatter() 65 | f.dateStyle = .medium 66 | let hiddenData:[String:AnyObject] = ["id":0 as AnyObject] 67 | let data:[String:AnyObject] = ["name":"Sebastian Gazolla Jr" as AnyObject, 68 | "address":"SQN 315" as AnyObject, 69 | "birthday":Date() as AnyObject, 70 | "company":"Pineapple Computers" as AnyObject, 71 | "position":"Software Engineer" as AnyObject, 72 | "salary":"200,000.00" as AnyObject, 73 | "count":0.0 as AnyObject, 74 | "Frequencia":50.0 as AnyObject] 75 | myForm.data = data 76 | myForm.hiddenData = hiddenData 77 | 78 | 79 | return myForm 80 | } 81 | 82 | func createTabBar()->UITabBarController { 83 | let delegateFormNav = UINavigationController(rootViewController: createForm()) 84 | let tabOneBarItem = UITabBarItem(title: "DelegateForm", image: UIImage(named: "DelegateForm"), selectedImage: UIImage(named: "DelegateForm")) 85 | delegateFormNav.tabBarItem = tabOneBarItem 86 | 87 | let tabTwoBarItem2 = UITabBarItem(title: "MyForm", image: UIImage(named: "MyForm"), selectedImage: UIImage(named: "MyForm")) 88 | let myFormNav = UINavigationController(rootViewController: MyFormViewController()) 89 | 90 | myFormNav.tabBarItem = tabTwoBarItem2 91 | 92 | let tabBarCtrl = UITabBarController() 93 | tabBarCtrl.viewControllers = [delegateFormNav,myFormNav] 94 | return tabBarCtrl 95 | } 96 | 97 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 98 | self.window = UIWindow(frame: UIScreen.main.bounds) 99 | self.window!.rootViewController = createTabBar() 100 | self.window!.backgroundColor = UIColor.white 101 | self.window!.makeKeyAndVisible() 102 | return true 103 | } 104 | 105 | func applicationWillResignActive(_ application: UIApplication) { 106 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 107 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 108 | } 109 | 110 | func applicationDidEnterBackground(_ application: UIApplication) { 111 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 112 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 113 | } 114 | 115 | func applicationWillEnterForeground(_ application: UIApplication) { 116 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 117 | } 118 | 119 | func applicationDidBecomeActive(_ application: UIApplication) { 120 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 121 | } 122 | 123 | func applicationWillTerminate(_ application: UIApplication) { 124 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 125 | } 126 | 127 | 128 | } 129 | 130 | -------------------------------------------------------------------------------- /TableForm/FormViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormViewController.swift 3 | // Form 4 | // 5 | // Created by Gazolla on 10/03/17. 6 | // Copyright © 2017 Gazolla. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct Field{ 12 | var name:String 13 | var title:String 14 | var value:AnyObject? 15 | var cellType:UITableViewCell.Type 16 | var cellId:String 17 | 18 | init(name:String, title:String, cellType: UITableViewCell.Type){ 19 | self.name = name 20 | self.title = title 21 | self.cellType = cellType 22 | cellId = "\(cellType.self)\(name)" 23 | } 24 | } 25 | 26 | public struct ConfigureForm{ 27 | var fields:[[Field]] 28 | var selectedRow:((_ form:FormViewController, _ indexPath:IndexPath)->())? 29 | var configureCell:((_ cell:FormCell, _ item:Field)->())? 30 | 31 | init (fields:[[Field]], selectedRow:((_ form:FormViewController, _ indexPath:IndexPath)->())?=nil, configureCell:((_ cell:UITableViewCell, _ item:Field)->())?=nil){ 32 | self.fields = fields 33 | self.selectedRow = selectedRow 34 | self.configureCell = configureCell 35 | } 36 | } 37 | 38 | 39 | class FormViewController: UIViewController { 40 | 41 | var hiddenData:[String:AnyObject?]? 42 | var fields:[[Field]]? 43 | var sections:[[FormCell]]? 44 | var selectedRow:((_ form:FormViewController, _ indexPath:IndexPath)->())? 45 | var configureCell:((_ cell:FormCell, _ item:Field)->())? 46 | var buildCellsDone:(()->())? 47 | var data:[String:AnyObject?]? 48 | 49 | 50 | lazy var tableView:FormView = { 51 | let tv = FormView(frame: self.view.bounds, style: .grouped) 52 | tv.translatesAutoresizingMaskIntoConstraints = false 53 | tv.delegate = self 54 | tv.dataSource = self 55 | tv.keyboardDismissMode = .onDrag 56 | 57 | tv.completion = { 58 | DispatchQueue.main.async { 59 | if self.data != nil { 60 | self.setFormData() 61 | } 62 | } 63 | } 64 | return tv 65 | }() 66 | 67 | init(){ 68 | super.init(nibName: nil, bundle: nil) 69 | } 70 | 71 | init(config:ConfigureForm){ 72 | self.fields = config.fields 73 | self.selectedRow = config.selectedRow 74 | super.init(nibName: nil, bundle: nil) 75 | self.configureCell = config.configureCell 76 | if let its = self.fields { 77 | self.sections = self.buildCells(items: its) 78 | } 79 | } 80 | 81 | required init?(coder aDecoder: NSCoder) { 82 | fatalError("init(coder:) has not been implemented") 83 | } 84 | 85 | func buildCells(items:[[Field]])->[[FormCell]]?{ 86 | var secs = [[FormCell]]() 87 | for section in items{ 88 | var c = [FormCell]() 89 | for it in section { 90 | let instance = it.cellType.init(style: .default, reuseIdentifier: it.cellId) as! FormCell 91 | instance.name = it.name 92 | instance.textLabel?.text = "\(it.title)" 93 | c.append(instance) 94 | } 95 | secs.append(c) 96 | } 97 | return secs 98 | } 99 | 100 | func incrementIndexPath(_ indexPath: IndexPath) -> IndexPath? { 101 | var nextIndexPath: IndexPath? 102 | let rowCount = tableView.numberOfRows(inSection: (indexPath as NSIndexPath).section) 103 | let nextRow = (indexPath as NSIndexPath).row + 1 104 | let currentSection = (indexPath as NSIndexPath).section 105 | 106 | if nextRow < rowCount { 107 | nextIndexPath = IndexPath(row: nextRow, section: currentSection) 108 | } 109 | else { 110 | let nextSection = currentSection + 1 111 | if nextSection < tableView.numberOfSections { 112 | nextIndexPath = IndexPath(row: 0, section: nextSection) 113 | } 114 | } 115 | return nextIndexPath 116 | } 117 | 118 | func getFormData()->[String:AnyObject?]{ 119 | if self.data == nil { self.data = [String:AnyObject?]() } 120 | var index:IndexPath? = IndexPath(row: 0, section: 0) 121 | while index != nil { 122 | let cell = self.tableView.cellForRow(at: index!) 123 | if cell is FormCell { 124 | let tuple = (cell as! FormCell).getCellData() 125 | self.data![tuple.key] = tuple.value as AnyObject? 126 | } 127 | index = self.incrementIndexPath(index!) 128 | } 129 | if let hiddenData = hiddenData { 130 | if hiddenData.count > 0{ 131 | for (key, value) in hiddenData { 132 | data![key] = value 133 | } 134 | } 135 | } 136 | print(data!) 137 | return self.data! 138 | } 139 | 140 | func setFormData(){ 141 | guard let sections = self.sections else { return } 142 | for (key, value) in self.data! { 143 | var index:IndexPath? = IndexPath(row: 0, section: 0) 144 | while index != nil { 145 | let cell = sections[(index! as NSIndexPath).section][(index! as NSIndexPath).item] 146 | if cell.name == key && value != nil { 147 | cell.setCellData(key: key, value: value!) 148 | } 149 | index = self.incrementIndexPath(index!) 150 | } 151 | } 152 | } 153 | 154 | override func viewDidLoad() { 155 | super.viewDidLoad() 156 | navigationController?.navigationBar.prefersLargeTitles = true 157 | self.view.addSubview(self.tableView) 158 | } 159 | 160 | override func viewDidAppear(_ animated: Bool) { 161 | super.viewDidAppear(animated) 162 | registerObservers() 163 | } 164 | 165 | override func viewDidDisappear(_ animated: Bool) { 166 | super.viewDidDisappear(animated) 167 | unregisterObservers() 168 | } 169 | 170 | override func updateViewConstraints() { 171 | super.updateViewConstraints() 172 | tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true 173 | tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true 174 | tableView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true 175 | tableView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true 176 | } 177 | 178 | 179 | func registerObservers() { 180 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) 181 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) 182 | NotificationCenter.default.addObserver(self, selector: #selector(textFieldTextDidEnd(_:)), name: NSNotification.Name.UITextFieldTextDidEndEditing, object: nil) 183 | } 184 | 185 | func unregisterObservers() { 186 | NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) 187 | NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) 188 | NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UITextFieldTextDidEndEditing, object: nil) 189 | } 190 | 191 | @objc func textFieldTextDidEnd(_ sender: NSNotification) { 192 | _ = self.getFormData() 193 | } 194 | 195 | @objc func keyboardWillShow(_ sender: NSNotification) { 196 | let info = sender.userInfo! 197 | let keyboardSize = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height 198 | 199 | tableView.contentInset.bottom = keyboardSize 200 | } 201 | 202 | @objc func keyboardWillHide(_ sender: NSNotification) { 203 | tableView.contentInset.bottom = 0 204 | _ = self.getFormData() 205 | } 206 | 207 | } 208 | 209 | extension FormViewController:UITableViewDelegate { 210 | 211 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 212 | guard let section = self.sections?[indexPath.section] else { return 0.0 } 213 | let cell = section[indexPath.item] 214 | if (cell is SliderCell) { 215 | return 70 216 | } else if (cell is ButtonCell) { 217 | return 60 218 | } else if ((cell is TextViewCell) || (cell is ImageCell)){ 219 | return 140 220 | } else { 221 | return 44 222 | } 223 | } 224 | 225 | func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 226 | return nil 227 | } 228 | 229 | func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { 230 | return nil 231 | } 232 | 233 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 234 | guard let item = sections?[section][0] else { return 0.0 } 235 | if item is ButtonCell { 236 | return 20 237 | } else { 238 | return 3 239 | } 240 | } 241 | 242 | func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 243 | guard let item = sections?[section][0] else { return 0.0 } 244 | if item is ButtonCell { 245 | return 20 246 | } else { 247 | return 3 248 | } 249 | } 250 | } 251 | 252 | extension FormViewController:UITableViewDataSource { 253 | 254 | func numberOfSections(in tableView: UITableView) -> Int { 255 | print("numberOfSections") 256 | print("sections: \(self.sections?.count ?? -1)") 257 | if self.sections == nil { return 0 } 258 | return self.fields?.count ?? 0 259 | } 260 | 261 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 262 | print("numberOfRowsInSection") 263 | print("sections: \(self.sections?.count ?? -1)") 264 | if self.sections == nil { return 0 } 265 | return self.fields?[section].count ?? 0 266 | } 267 | 268 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 269 | guard let items = self.fields, let sections = self.sections else { return UITableViewCell() } 270 | let cell = sections[(indexPath as NSIndexPath).section][(indexPath as NSIndexPath).item] 271 | let item = items[indexPath.section][indexPath.item] 272 | configureCell?(cell, item) 273 | if indexPath.row + 1 == items[indexPath.section].count { 274 | cell.tag = 999 275 | } else { 276 | cell.tag = 0 277 | } 278 | return cell 279 | } 280 | 281 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 282 | for cell in tableView.visibleCells { 283 | if cell is TextCell{ 284 | if ((cell as! TextCell).textField.isFirstResponder) == true{ 285 | (cell as! TextCell).textField.resignFirstResponder() 286 | } 287 | } 288 | } 289 | selectedRow?(self, indexPath) 290 | } 291 | } 292 | 293 | -------------------------------------------------------------------------------- /TableForm.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 08177D201D46A3B000228592 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08177D1F1D46A3B000228592 /* AppDelegate.swift */; }; 11 | 08177D221D46A3B000228592 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 08177D211D46A3B000228592 /* Assets.xcassets */; }; 12 | 08177D251D46A3B000228592 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 08177D231D46A3B000228592 /* LaunchScreen.storyboard */; }; 13 | 08177D301D46A3B000228592 /* TableFormTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08177D2F1D46A3B000228592 /* TableFormTests.swift */; }; 14 | 08177D3B1D46A3B000228592 /* TableFormUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08177D3A1D46A3B000228592 /* TableFormUITests.swift */; }; 15 | 08177D491D46A3BD00228592 /* FormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08177D481D46A3BD00228592 /* FormViewController.swift */; }; 16 | 08177D4B1D46DD3A00228592 /* FormCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08177D4A1D46DD3A00228592 /* FormCells.swift */; }; 17 | 08284B191F8688FD00367E83 /* MyFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08284B181F8688FD00367E83 /* MyFormView.swift */; }; 18 | 08523E851F8969D8008D444D /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08523E841F8969D8008D444D /* TableViewController.swift */; }; 19 | 0860B2891F86D01E00D76378 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 0860B2881F86D01E00D76378 /* README.md */; }; 20 | 0882883E1E738F1B00DDA1C6 /* FormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0882883D1E738F1B00DDA1C6 /* FormView.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 08177D2C1D46A3B000228592 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 08177D141D46A3B000228592 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 08177D1B1D46A3B000228592; 29 | remoteInfo = TableForm; 30 | }; 31 | 08177D371D46A3B000228592 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 08177D141D46A3B000228592 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 08177D1B1D46A3B000228592; 36 | remoteInfo = TableForm; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 08177D1C1D46A3B000228592 /* TableForm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TableForm.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 08177D1F1D46A3B000228592 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43 | 08177D211D46A3B000228592 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 44 | 08177D241D46A3B000228592 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 45 | 08177D261D46A3B000228592 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | 08177D2B1D46A3B000228592 /* TableFormTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableFormTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 08177D2F1D46A3B000228592 /* TableFormTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableFormTests.swift; sourceTree = ""; }; 48 | 08177D311D46A3B000228592 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | 08177D361D46A3B000228592 /* TableFormUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableFormUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 08177D3A1D46A3B000228592 /* TableFormUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableFormUITests.swift; sourceTree = ""; }; 51 | 08177D3C1D46A3B000228592 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | 08177D481D46A3BD00228592 /* FormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormViewController.swift; sourceTree = ""; }; 53 | 08177D4A1D46DD3A00228592 /* FormCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormCells.swift; sourceTree = ""; }; 54 | 08284B181F8688FD00367E83 /* MyFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyFormView.swift; sourceTree = ""; }; 55 | 08523E841F8969D8008D444D /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 56 | 0860B2881F86D01E00D76378 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 57 | 0882883D1E738F1B00DDA1C6 /* FormView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormView.swift; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 08177D191D46A3B000228592 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | 08177D281D46A3B000228592 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | 08177D331D46A3B000228592 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 08177D131D46A3B000228592 = { 86 | isa = PBXGroup; 87 | children = ( 88 | 0860B2881F86D01E00D76378 /* README.md */, 89 | 08177D1E1D46A3B000228592 /* TableForm */, 90 | 08177D2E1D46A3B000228592 /* TableFormTests */, 91 | 08177D391D46A3B000228592 /* TableFormUITests */, 92 | 08177D1D1D46A3B000228592 /* Products */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 08177D1D1D46A3B000228592 /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 08177D1C1D46A3B000228592 /* TableForm.app */, 100 | 08177D2B1D46A3B000228592 /* TableFormTests.xctest */, 101 | 08177D361D46A3B000228592 /* TableFormUITests.xctest */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | 08177D1E1D46A3B000228592 /* TableForm */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 08523E841F8969D8008D444D /* TableViewController.swift */, 110 | 08177D1F1D46A3B000228592 /* AppDelegate.swift */, 111 | 08177D211D46A3B000228592 /* Assets.xcassets */, 112 | 08177D231D46A3B000228592 /* LaunchScreen.storyboard */, 113 | 08177D261D46A3B000228592 /* Info.plist */, 114 | 0882883D1E738F1B00DDA1C6 /* FormView.swift */, 115 | 08177D481D46A3BD00228592 /* FormViewController.swift */, 116 | 08177D4A1D46DD3A00228592 /* FormCells.swift */, 117 | 08284B181F8688FD00367E83 /* MyFormView.swift */, 118 | ); 119 | path = TableForm; 120 | sourceTree = ""; 121 | }; 122 | 08177D2E1D46A3B000228592 /* TableFormTests */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 08177D2F1D46A3B000228592 /* TableFormTests.swift */, 126 | 08177D311D46A3B000228592 /* Info.plist */, 127 | ); 128 | path = TableFormTests; 129 | sourceTree = ""; 130 | }; 131 | 08177D391D46A3B000228592 /* TableFormUITests */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 08177D3A1D46A3B000228592 /* TableFormUITests.swift */, 135 | 08177D3C1D46A3B000228592 /* Info.plist */, 136 | ); 137 | path = TableFormUITests; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 08177D1B1D46A3B000228592 /* TableForm */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 08177D3F1D46A3B000228592 /* Build configuration list for PBXNativeTarget "TableForm" */; 146 | buildPhases = ( 147 | 08177D181D46A3B000228592 /* Sources */, 148 | 08177D191D46A3B000228592 /* Frameworks */, 149 | 08177D1A1D46A3B000228592 /* Resources */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = TableForm; 156 | productName = TableForm; 157 | productReference = 08177D1C1D46A3B000228592 /* TableForm.app */; 158 | productType = "com.apple.product-type.application"; 159 | }; 160 | 08177D2A1D46A3B000228592 /* TableFormTests */ = { 161 | isa = PBXNativeTarget; 162 | buildConfigurationList = 08177D421D46A3B000228592 /* Build configuration list for PBXNativeTarget "TableFormTests" */; 163 | buildPhases = ( 164 | 08177D271D46A3B000228592 /* Sources */, 165 | 08177D281D46A3B000228592 /* Frameworks */, 166 | 08177D291D46A3B000228592 /* Resources */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | 08177D2D1D46A3B000228592 /* PBXTargetDependency */, 172 | ); 173 | name = TableFormTests; 174 | productName = TableFormTests; 175 | productReference = 08177D2B1D46A3B000228592 /* TableFormTests.xctest */; 176 | productType = "com.apple.product-type.bundle.unit-test"; 177 | }; 178 | 08177D351D46A3B000228592 /* TableFormUITests */ = { 179 | isa = PBXNativeTarget; 180 | buildConfigurationList = 08177D451D46A3B000228592 /* Build configuration list for PBXNativeTarget "TableFormUITests" */; 181 | buildPhases = ( 182 | 08177D321D46A3B000228592 /* Sources */, 183 | 08177D331D46A3B000228592 /* Frameworks */, 184 | 08177D341D46A3B000228592 /* Resources */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | 08177D381D46A3B000228592 /* PBXTargetDependency */, 190 | ); 191 | name = TableFormUITests; 192 | productName = TableFormUITests; 193 | productReference = 08177D361D46A3B000228592 /* TableFormUITests.xctest */; 194 | productType = "com.apple.product-type.bundle.ui-testing"; 195 | }; 196 | /* End PBXNativeTarget section */ 197 | 198 | /* Begin PBXProject section */ 199 | 08177D141D46A3B000228592 /* Project object */ = { 200 | isa = PBXProject; 201 | attributes = { 202 | LastSwiftUpdateCheck = 0730; 203 | LastUpgradeCheck = 0930; 204 | ORGANIZATIONNAME = Gazolla; 205 | TargetAttributes = { 206 | 08177D1B1D46A3B000228592 = { 207 | CreatedOnToolsVersion = 7.3.1; 208 | DevelopmentTeam = MA5BADG5AW; 209 | LastSwiftMigration = 0900; 210 | }; 211 | 08177D2A1D46A3B000228592 = { 212 | CreatedOnToolsVersion = 7.3.1; 213 | LastSwiftMigration = 0900; 214 | TestTargetID = 08177D1B1D46A3B000228592; 215 | }; 216 | 08177D351D46A3B000228592 = { 217 | CreatedOnToolsVersion = 7.3.1; 218 | LastSwiftMigration = 0900; 219 | TestTargetID = 08177D1B1D46A3B000228592; 220 | }; 221 | }; 222 | }; 223 | buildConfigurationList = 08177D171D46A3B000228592 /* Build configuration list for PBXProject "TableForm" */; 224 | compatibilityVersion = "Xcode 3.2"; 225 | developmentRegion = English; 226 | hasScannedForEncodings = 0; 227 | knownRegions = ( 228 | en, 229 | Base, 230 | ); 231 | mainGroup = 08177D131D46A3B000228592; 232 | productRefGroup = 08177D1D1D46A3B000228592 /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | 08177D1B1D46A3B000228592 /* TableForm */, 237 | 08177D2A1D46A3B000228592 /* TableFormTests */, 238 | 08177D351D46A3B000228592 /* TableFormUITests */, 239 | ); 240 | }; 241 | /* End PBXProject section */ 242 | 243 | /* Begin PBXResourcesBuildPhase section */ 244 | 08177D1A1D46A3B000228592 /* Resources */ = { 245 | isa = PBXResourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 0860B2891F86D01E00D76378 /* README.md in Resources */, 249 | 08177D221D46A3B000228592 /* Assets.xcassets in Resources */, 250 | 08177D251D46A3B000228592 /* LaunchScreen.storyboard in Resources */, 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | }; 254 | 08177D291D46A3B000228592 /* Resources */ = { 255 | isa = PBXResourcesBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | 08177D341D46A3B000228592 /* Resources */ = { 262 | isa = PBXResourcesBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | }; 268 | /* End PBXResourcesBuildPhase section */ 269 | 270 | /* Begin PBXSourcesBuildPhase section */ 271 | 08177D181D46A3B000228592 /* Sources */ = { 272 | isa = PBXSourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | 08284B191F8688FD00367E83 /* MyFormView.swift in Sources */, 276 | 0882883E1E738F1B00DDA1C6 /* FormView.swift in Sources */, 277 | 08177D201D46A3B000228592 /* AppDelegate.swift in Sources */, 278 | 08523E851F8969D8008D444D /* TableViewController.swift in Sources */, 279 | 08177D4B1D46DD3A00228592 /* FormCells.swift in Sources */, 280 | 08177D491D46A3BD00228592 /* FormViewController.swift in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | 08177D271D46A3B000228592 /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 08177D301D46A3B000228592 /* TableFormTests.swift in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | 08177D321D46A3B000228592 /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | 08177D3B1D46A3B000228592 /* TableFormUITests.swift in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXSourcesBuildPhase section */ 301 | 302 | /* Begin PBXTargetDependency section */ 303 | 08177D2D1D46A3B000228592 /* PBXTargetDependency */ = { 304 | isa = PBXTargetDependency; 305 | target = 08177D1B1D46A3B000228592 /* TableForm */; 306 | targetProxy = 08177D2C1D46A3B000228592 /* PBXContainerItemProxy */; 307 | }; 308 | 08177D381D46A3B000228592 /* PBXTargetDependency */ = { 309 | isa = PBXTargetDependency; 310 | target = 08177D1B1D46A3B000228592 /* TableForm */; 311 | targetProxy = 08177D371D46A3B000228592 /* PBXContainerItemProxy */; 312 | }; 313 | /* End PBXTargetDependency section */ 314 | 315 | /* Begin PBXVariantGroup section */ 316 | 08177D231D46A3B000228592 /* LaunchScreen.storyboard */ = { 317 | isa = PBXVariantGroup; 318 | children = ( 319 | 08177D241D46A3B000228592 /* Base */, 320 | ); 321 | name = LaunchScreen.storyboard; 322 | sourceTree = ""; 323 | }; 324 | /* End PBXVariantGroup section */ 325 | 326 | /* Begin XCBuildConfiguration section */ 327 | 08177D3D1D46A3B000228592 /* Debug */ = { 328 | isa = XCBuildConfiguration; 329 | buildSettings = { 330 | ALWAYS_SEARCH_USER_PATHS = NO; 331 | CLANG_ANALYZER_NONNULL = YES; 332 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 333 | CLANG_CXX_LIBRARY = "libc++"; 334 | CLANG_ENABLE_MODULES = YES; 335 | CLANG_ENABLE_OBJC_ARC = YES; 336 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 337 | CLANG_WARN_BOOL_CONVERSION = YES; 338 | CLANG_WARN_COMMA = YES; 339 | CLANG_WARN_CONSTANT_CONVERSION = YES; 340 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 341 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 342 | CLANG_WARN_EMPTY_BODY = YES; 343 | CLANG_WARN_ENUM_CONVERSION = YES; 344 | CLANG_WARN_INFINITE_RECURSION = YES; 345 | CLANG_WARN_INT_CONVERSION = YES; 346 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 347 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 348 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 349 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 350 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 351 | CLANG_WARN_STRICT_PROTOTYPES = YES; 352 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 353 | CLANG_WARN_UNREACHABLE_CODE = YES; 354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 355 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 356 | COPY_PHASE_STRIP = NO; 357 | DEBUG_INFORMATION_FORMAT = dwarf; 358 | ENABLE_STRICT_OBJC_MSGSEND = YES; 359 | ENABLE_TESTABILITY = YES; 360 | GCC_C_LANGUAGE_STANDARD = gnu99; 361 | GCC_DYNAMIC_NO_PIC = NO; 362 | GCC_NO_COMMON_BLOCKS = YES; 363 | GCC_OPTIMIZATION_LEVEL = 0; 364 | GCC_PREPROCESSOR_DEFINITIONS = ( 365 | "DEBUG=1", 366 | "$(inherited)", 367 | ); 368 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 369 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 370 | GCC_WARN_UNDECLARED_SELECTOR = YES; 371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 372 | GCC_WARN_UNUSED_FUNCTION = YES; 373 | GCC_WARN_UNUSED_VARIABLE = YES; 374 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 375 | MTL_ENABLE_DEBUG_INFO = YES; 376 | ONLY_ACTIVE_ARCH = YES; 377 | SDKROOT = iphoneos; 378 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 379 | TARGETED_DEVICE_FAMILY = "1,2"; 380 | }; 381 | name = Debug; 382 | }; 383 | 08177D3E1D46A3B000228592 /* Release */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | ALWAYS_SEARCH_USER_PATHS = NO; 387 | CLANG_ANALYZER_NONNULL = YES; 388 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 389 | CLANG_CXX_LIBRARY = "libc++"; 390 | CLANG_ENABLE_MODULES = YES; 391 | CLANG_ENABLE_OBJC_ARC = YES; 392 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 393 | CLANG_WARN_BOOL_CONVERSION = YES; 394 | CLANG_WARN_COMMA = YES; 395 | CLANG_WARN_CONSTANT_CONVERSION = YES; 396 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 397 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 398 | CLANG_WARN_EMPTY_BODY = YES; 399 | CLANG_WARN_ENUM_CONVERSION = YES; 400 | CLANG_WARN_INFINITE_RECURSION = YES; 401 | CLANG_WARN_INT_CONVERSION = YES; 402 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 403 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 404 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 405 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 406 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 407 | CLANG_WARN_STRICT_PROTOTYPES = YES; 408 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 409 | CLANG_WARN_UNREACHABLE_CODE = YES; 410 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 411 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 412 | COPY_PHASE_STRIP = NO; 413 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 414 | ENABLE_NS_ASSERTIONS = NO; 415 | ENABLE_STRICT_OBJC_MSGSEND = YES; 416 | GCC_C_LANGUAGE_STANDARD = gnu99; 417 | GCC_NO_COMMON_BLOCKS = YES; 418 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 419 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 420 | GCC_WARN_UNDECLARED_SELECTOR = YES; 421 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 422 | GCC_WARN_UNUSED_FUNCTION = YES; 423 | GCC_WARN_UNUSED_VARIABLE = YES; 424 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 425 | MTL_ENABLE_DEBUG_INFO = NO; 426 | SDKROOT = iphoneos; 427 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 428 | TARGETED_DEVICE_FAMILY = "1,2"; 429 | VALIDATE_PRODUCT = YES; 430 | }; 431 | name = Release; 432 | }; 433 | 08177D401D46A3B000228592 /* Debug */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 437 | CODE_SIGN_IDENTITY = "iPhone Developer"; 438 | INFOPLIST_FILE = TableForm/Info.plist; 439 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 440 | PRODUCT_BUNDLE_IDENTIFIER = com.gazapps.TableForm; 441 | PRODUCT_NAME = "$(TARGET_NAME)"; 442 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 443 | SWIFT_VERSION = 4.0; 444 | }; 445 | name = Debug; 446 | }; 447 | 08177D411D46A3B000228592 /* Release */ = { 448 | isa = XCBuildConfiguration; 449 | buildSettings = { 450 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 451 | CODE_SIGN_IDENTITY = "iPhone Developer"; 452 | INFOPLIST_FILE = TableForm/Info.plist; 453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 454 | PRODUCT_BUNDLE_IDENTIFIER = com.gazapps.TableForm; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 457 | SWIFT_VERSION = 4.0; 458 | }; 459 | name = Release; 460 | }; 461 | 08177D431D46A3B000228592 /* Debug */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | BUNDLE_LOADER = "$(TEST_HOST)"; 465 | INFOPLIST_FILE = TableFormTests/Info.plist; 466 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 467 | PRODUCT_BUNDLE_IDENTIFIER = com.gazapps.TableFormTests; 468 | PRODUCT_NAME = "$(TARGET_NAME)"; 469 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 470 | SWIFT_VERSION = 4.0; 471 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TableForm.app/TableForm"; 472 | }; 473 | name = Debug; 474 | }; 475 | 08177D441D46A3B000228592 /* Release */ = { 476 | isa = XCBuildConfiguration; 477 | buildSettings = { 478 | BUNDLE_LOADER = "$(TEST_HOST)"; 479 | INFOPLIST_FILE = TableFormTests/Info.plist; 480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 481 | PRODUCT_BUNDLE_IDENTIFIER = com.gazapps.TableFormTests; 482 | PRODUCT_NAME = "$(TARGET_NAME)"; 483 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 484 | SWIFT_VERSION = 4.0; 485 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TableForm.app/TableForm"; 486 | }; 487 | name = Release; 488 | }; 489 | 08177D461D46A3B000228592 /* Debug */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | INFOPLIST_FILE = TableFormUITests/Info.plist; 493 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 494 | PRODUCT_BUNDLE_IDENTIFIER = com.gazapps.TableFormUITests; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 497 | SWIFT_VERSION = 4.0; 498 | TEST_TARGET_NAME = TableForm; 499 | }; 500 | name = Debug; 501 | }; 502 | 08177D471D46A3B000228592 /* Release */ = { 503 | isa = XCBuildConfiguration; 504 | buildSettings = { 505 | INFOPLIST_FILE = TableFormUITests/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 507 | PRODUCT_BUNDLE_IDENTIFIER = com.gazapps.TableFormUITests; 508 | PRODUCT_NAME = "$(TARGET_NAME)"; 509 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 510 | SWIFT_VERSION = 4.0; 511 | TEST_TARGET_NAME = TableForm; 512 | }; 513 | name = Release; 514 | }; 515 | /* End XCBuildConfiguration section */ 516 | 517 | /* Begin XCConfigurationList section */ 518 | 08177D171D46A3B000228592 /* Build configuration list for PBXProject "TableForm" */ = { 519 | isa = XCConfigurationList; 520 | buildConfigurations = ( 521 | 08177D3D1D46A3B000228592 /* Debug */, 522 | 08177D3E1D46A3B000228592 /* Release */, 523 | ); 524 | defaultConfigurationIsVisible = 0; 525 | defaultConfigurationName = Release; 526 | }; 527 | 08177D3F1D46A3B000228592 /* Build configuration list for PBXNativeTarget "TableForm" */ = { 528 | isa = XCConfigurationList; 529 | buildConfigurations = ( 530 | 08177D401D46A3B000228592 /* Debug */, 531 | 08177D411D46A3B000228592 /* Release */, 532 | ); 533 | defaultConfigurationIsVisible = 0; 534 | defaultConfigurationName = Release; 535 | }; 536 | 08177D421D46A3B000228592 /* Build configuration list for PBXNativeTarget "TableFormTests" */ = { 537 | isa = XCConfigurationList; 538 | buildConfigurations = ( 539 | 08177D431D46A3B000228592 /* Debug */, 540 | 08177D441D46A3B000228592 /* Release */, 541 | ); 542 | defaultConfigurationIsVisible = 0; 543 | defaultConfigurationName = Release; 544 | }; 545 | 08177D451D46A3B000228592 /* Build configuration list for PBXNativeTarget "TableFormUITests" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 08177D461D46A3B000228592 /* Debug */, 549 | 08177D471D46A3B000228592 /* Release */, 550 | ); 551 | defaultConfigurationIsVisible = 0; 552 | defaultConfigurationName = Release; 553 | }; 554 | /* End XCConfigurationList section */ 555 | }; 556 | rootObject = 08177D141D46A3B000228592 /* Project object */; 557 | } 558 | -------------------------------------------------------------------------------- /TableForm/FormCells.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextCell.swift 3 | // TableForm 4 | // 5 | // Created by Gazolla on 05/10/17. 6 | // Copyright © 2016 Gazolla. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import QuartzCore 11 | 12 | open class FormCell:UITableViewCell { 13 | var name:String? 14 | 15 | lazy var space:UIView = { 16 | let v = UIView() 17 | v.widthAnchor.constraint(equalToConstant: self.bounds.width*0.001).isActive = true 18 | return v 19 | }() 20 | 21 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 22 | super.init(style: .default, reuseIdentifier: reuseIdentifier) 23 | self.setup() 24 | } 25 | 26 | required public init?(coder aDecoder: NSCoder) { 27 | super.init(coder: aDecoder) 28 | self.setup() 29 | } 30 | 31 | override open func awakeFromNib() { 32 | super.awakeFromNib() 33 | self.setup() 34 | } 35 | 36 | func setup() { 37 | self.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body) 38 | self.textLabel?.textColor = .black 39 | } 40 | 41 | func setCellData(key: String, value: AnyObject){ } 42 | 43 | func getCellData()-> (key: String, value: AnyObject){ 44 | let key = "" 45 | let value = "" as AnyObject 46 | return(key, value) 47 | } 48 | 49 | open override func layoutSubviews() { 50 | super.layoutSubviews() 51 | if self.tag == 999 { //botton cell 52 | layer.shadowColor = UIColor.lightGray.cgColor 53 | layer.shadowOffset = CGSize(width: 0, height: 2.0) 54 | layer.shadowRadius = 2.0 55 | layer.shadowOpacity = 1.0 56 | layer.masksToBounds = false 57 | layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: contentView.layer.cornerRadius).cgPath 58 | } 59 | } 60 | 61 | } 62 | 63 | open class TextViewCell: FormCell, UITextViewDelegate { 64 | 65 | var textView = UITextView() 66 | 67 | override func setup() { 68 | super.setup() 69 | 70 | self.contentView.viewWithTag(3)?.removeFromSuperview() 71 | textView.autocorrectionType = .no 72 | textView.autocapitalizationType = .none 73 | self.textView.delegate = self 74 | self.textView.tag = 3 75 | self.textView.translatesAutoresizingMaskIntoConstraints = false 76 | self.addSubview(self.textView) 77 | 78 | addConstraintsWithFormat("V:|-3-[v0]-3-|", views:self.textView) 79 | addConstraintsWithFormat("H:|-100-[v0]-5-|", views:self.textView) 80 | 81 | self.textView.textAlignment = .left 82 | } 83 | 84 | override open func touchesBegan(_ touches: Set, with event: UIEvent?) { 85 | self.textView.becomeFirstResponder() 86 | } 87 | 88 | public func textViewDidEndEditing(_ textView: UITextView) { 89 | textView.resignFirstResponder() 90 | } 91 | 92 | override func setCellData(key: String, value: AnyObject){ 93 | self.textView.text! = value as! String 94 | } 95 | 96 | override func getCellData()-> (key: String, value: AnyObject){ 97 | let key = self.name! 98 | let value = self.textView.text as AnyObject 99 | return(key, value) 100 | } 101 | 102 | public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { 103 | var view = self.superview 104 | while (view != nil && view!.isKind(of: UITableView.self) == false) { 105 | view = view!.superview 106 | } 107 | let tableView = view as? UITableView 108 | 109 | let txtFieldPosition = textView.convert(textView.bounds.origin, to: tableView) 110 | let indexPath = tableView?.indexPathForRow(at: txtFieldPosition) 111 | if indexPath != nil { 112 | tableView?.scrollToRow(at: indexPath!, at: .top, animated: true) 113 | } 114 | return true 115 | } 116 | } 117 | 118 | open class ImageCell: FormCell, UITextViewDelegate { 119 | 120 | var imgView = UIImageView() 121 | var fileName:String = "" 122 | 123 | override func setup() { 124 | super.setup() 125 | self.contentView.viewWithTag(3)?.removeFromSuperview() 126 | imgView.backgroundColor = .white 127 | imgView.contentMode = .scaleAspectFit 128 | imgView.translatesAutoresizingMaskIntoConstraints = false 129 | self.addSubview(self.imgView) 130 | 131 | addConstraintsWithFormat("V:|-3-[v0]-3-|", views:self.imgView) 132 | addConstraintsWithFormat("H:|-100-[v0]-5-|", views:self.imgView) 133 | } 134 | 135 | 136 | override func setCellData(key: String, value: AnyObject){ 137 | fileName = value as! String 138 | imgView.image = UIImage(named: fileName) 139 | } 140 | 141 | override func getCellData()-> (key: String, value: AnyObject){ 142 | let key = self.name! 143 | let value = fileName as AnyObject 144 | return(key, value) 145 | } 146 | 147 | } 148 | 149 | open class TextCell: FormCell, UITextFieldDelegate { 150 | 151 | var textField:UITextField = { 152 | let tf = UITextField() 153 | tf.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body) 154 | tf.autocorrectionType = .no 155 | tf.autocapitalizationType = .none 156 | tf.tag = 3 157 | tf.translatesAutoresizingMaskIntoConstraints = false 158 | tf.textAlignment = .left 159 | tf.contentVerticalAlignment = .center 160 | return tf 161 | }() 162 | 163 | override func setup() { 164 | super.setup() 165 | contentView.viewWithTag(3)?.removeFromSuperview() 166 | textField.delegate = self 167 | addSubview(textField) 168 | 169 | textField.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true 170 | textField.rightAnchor.constraint(equalTo: self.rightAnchor, constant:-5).isActive = true 171 | textField.leftAnchor.constraint(equalTo: self.leftAnchor, constant:115).isActive = true 172 | textField.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true 173 | 174 | } 175 | 176 | override open func touchesBegan(_ touches: Set, with event: UIEvent?) { 177 | self.textField.becomeFirstResponder() 178 | } 179 | 180 | open func textFieldShouldReturn(_ textField: UITextField) -> Bool { 181 | print("textFieldShouldReturn") 182 | textField.resignFirstResponder() 183 | return true 184 | } 185 | 186 | override func setCellData(key: String, value: AnyObject){ 187 | if let strValue = value as? String { 188 | self.textField.text! = strValue 189 | } 190 | } 191 | 192 | override func getCellData()-> (key: String, value: AnyObject){ 193 | let key = self.name! 194 | let value = self.textField.text as AnyObject 195 | return(key, value) 196 | } 197 | 198 | public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { 199 | var view = self.superview 200 | while (view != nil && view!.isKind(of: UITableView.self) == false) { 201 | view = view!.superview 202 | } 203 | let tableView = view as? UITableView 204 | 205 | let txtFieldPosition = textField.convert(textField.bounds.origin, to: tableView) 206 | let indexPath = tableView?.indexPathForRow(at: txtFieldPosition) 207 | if indexPath != nil { 208 | tableView?.scrollToRow(at: indexPath!, at: .top, animated: true) 209 | } 210 | return true 211 | } 212 | 213 | 214 | } 215 | 216 | open class NumberCell: FormCell, UITextFieldDelegate { 217 | 218 | lazy var numberFormatted:UITextField = { 219 | let nf = UITextField() 220 | nf.delegate = self 221 | nf.tag = 3 222 | nf.translatesAutoresizingMaskIntoConstraints = false 223 | nf.placeholder = "0.00" 224 | nf.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.title1) 225 | nf.autocorrectionType = UITextAutocorrectionType.no 226 | nf.keyboardType = UIKeyboardType.numberPad 227 | nf.returnKeyType = UIReturnKeyType.done 228 | nf.clearButtonMode = UITextFieldViewMode.whileEditing 229 | nf.contentVerticalAlignment = UIControlContentVerticalAlignment.center 230 | nf.textAlignment = NSTextAlignment.center 231 | return nf 232 | }() 233 | 234 | let formatter: NumberFormatter = { 235 | let formatter = NumberFormatter() 236 | formatter.numberStyle = .currency 237 | formatter.currencySymbol = "" 238 | formatter.maximumFractionDigits = 2 239 | formatter.minimumFractionDigits = 2 240 | return formatter 241 | }() 242 | 243 | var amountTypedString = "" 244 | 245 | override func setup() { 246 | super.setup() 247 | self.addSubview(numberFormatted) 248 | 249 | numberFormatted.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true 250 | numberFormatted.rightAnchor.constraint(equalTo: self.rightAnchor, constant:-5).isActive = true 251 | numberFormatted.leftAnchor.constraint(equalTo: self.leftAnchor, constant:115).isActive = true 252 | numberFormatted.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true 253 | } 254 | 255 | override open func touchesBegan(_ touches: Set, with event: UIEvent?) { 256 | numberFormatted.becomeFirstResponder() 257 | } 258 | 259 | open func textFieldShouldReturn(_ textField: UITextField) -> Bool { 260 | textField.resignFirstResponder() 261 | return true 262 | } 263 | 264 | public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 265 | // print("amountTypedString:\(amountTypedString)") 266 | if string.count > 0 { 267 | print(string) 268 | amountTypedString += string 269 | print(amountTypedString) 270 | let decNumber = NSDecimalNumber(value: Float(amountTypedString)! as Float).multiplying(by: 0.01) 271 | print(decNumber) 272 | let newString = formatter.string(from: decNumber)! 273 | print(newString) 274 | numberFormatted.text = newString 275 | } else { 276 | amountTypedString = String(amountTypedString.dropLast()) 277 | if amountTypedString.count > 0 { 278 | let decNumber = NSDecimalNumber(value: Float(amountTypedString)! as Float).multiplying(by: 0.01) 279 | let newString = formatter.string(from: decNumber)! 280 | numberFormatted.text = newString 281 | } else { 282 | numberFormatted.text = "0.00" 283 | } 284 | 285 | } 286 | return false 287 | } 288 | 289 | public func textFieldShouldClear(_ textField: UITextField) -> Bool { 290 | amountTypedString = "" 291 | return true 292 | } 293 | 294 | override func setCellData(key: String, value: AnyObject){ 295 | var strValue = "" 296 | if value is String{ 297 | strValue = value as! String 298 | } else if value is Double { 299 | strValue = String(format: "%\(0.2)f", (value as! Double)) 300 | // strValue = "\(value)" 301 | } 302 | numberFormatted.text! = strValue 303 | let charsNotToBeTrimmed = (0...9).map{String($0)} 304 | for i in strValue{ 305 | if !charsNotToBeTrimmed.contains(String(i)){ 306 | strValue = strValue.replacingOccurrences(of: String(i), with: "") 307 | } 308 | } 309 | // print("filtered: \(strValue)") 310 | amountTypedString = strValue 311 | // print("amountTypedString:\(amountTypedString)") 312 | } 313 | 314 | override func getCellData()-> (key: String, value: AnyObject){ 315 | let key = self.name! 316 | let value = numberFormatted.text as AnyObject 317 | return(key, value) 318 | } 319 | 320 | 321 | } 322 | 323 | open class IntCell : TextCell { 324 | 325 | override func setup() { 326 | super.setup() 327 | textField.textAlignment = .right 328 | textField.autocorrectionType = .default 329 | textField.autocapitalizationType = .none 330 | textField.keyboardType = .numberPad 331 | } 332 | 333 | override func setCellData(key: String, value: AnyObject){ 334 | let nf = NumberFormatter() 335 | self.textField.text! = nf.string(from: value as! NSNumber)! 336 | } 337 | 338 | } 339 | 340 | open class PhoneCell : TextCell { 341 | 342 | override func setup() { 343 | super.setup() 344 | textField.keyboardType = .phonePad 345 | } 346 | } 347 | 348 | open class NameCell : TextCell { 349 | 350 | open override func setup() { 351 | super.setup() 352 | textField.autocorrectionType = .no 353 | textField.autocapitalizationType = .words 354 | textField.keyboardType = .asciiCapable 355 | } 356 | 357 | override open func textFieldShouldReturn(_ textField: UITextField) -> Bool { 358 | print("textFieldShouldReturn") 359 | textField.resignFirstResponder() 360 | return true 361 | } 362 | } 363 | 364 | open class EmailCell : TextCell { 365 | 366 | open override func setup() { 367 | super.setup() 368 | textField.autocorrectionType = .no 369 | textField.autocapitalizationType = .none 370 | textField.keyboardType = .emailAddress 371 | } 372 | } 373 | 374 | open class PasswordCell : TextCell { 375 | 376 | open override func setup() { 377 | super.setup() 378 | textField.autocorrectionType = .no 379 | textField.autocapitalizationType = .none 380 | textField.keyboardType = .asciiCapable 381 | textField.isSecureTextEntry = true 382 | } 383 | } 384 | 385 | open class DecimalCell : TextCell { 386 | 387 | open override func setup() { 388 | super.setup() 389 | textField.autocorrectionType = .no 390 | textField.keyboardType = .decimalPad 391 | } 392 | } 393 | 394 | open class URLCell : TextCell { 395 | 396 | open override func setup() { 397 | super.setup() 398 | textField.autocorrectionType = .no 399 | textField.autocapitalizationType = .none 400 | textField.keyboardType = .URL 401 | } 402 | } 403 | 404 | open class TwitterCell : TextCell { 405 | 406 | open override func setup() { 407 | super.setup() 408 | textField.autocorrectionType = .no 409 | textField.autocapitalizationType = .none 410 | textField.keyboardType = .twitter 411 | } 412 | } 413 | 414 | open class AccountCell : TextCell { 415 | 416 | open override func setup() { 417 | super.setup() 418 | textField.autocorrectionType = .no 419 | textField.autocapitalizationType = .none 420 | textField.keyboardType = .asciiCapable 421 | } 422 | } 423 | 424 | open class ZipCodeCell : TextCell { 425 | 426 | open override func setup() { 427 | super.setup() 428 | textField.autocorrectionType = .no 429 | textField.autocapitalizationType = .allCharacters 430 | textField.keyboardType = .numbersAndPunctuation 431 | } 432 | } 433 | 434 | open class DateCell : TextCell { 435 | 436 | var update:((_ date:Date)->())? 437 | 438 | var datePicker = UIDatePicker() 439 | var formatter:DateFormatter { 440 | let f = DateFormatter() 441 | f.dateStyle = .medium 442 | return f 443 | } 444 | 445 | override func setup() { 446 | super.setup() 447 | 448 | self.textField.textAlignment = .right 449 | 450 | accessoryType = .none 451 | editingAccessoryType = .none 452 | datePicker.datePickerMode = UIDatePickerMode.date 453 | self.textField.inputView = datePicker 454 | datePicker.addTarget(self, action: #selector(DateCell.datePickerValueChanged(_:)), for: .valueChanged) 455 | } 456 | 457 | deinit { 458 | datePicker.removeTarget(self, action: nil, for: .allEvents) 459 | } 460 | 461 | @objc func datePickerValueChanged(_ sender: UIDatePicker){ 462 | self.textField.text = formatter.string(from: sender.date) 463 | // self.update?(sender.date) 464 | } 465 | 466 | override func setCellData(key: String, value: AnyObject){ 467 | if let dateValue = value as? Date { 468 | self.datePicker.date = dateValue 469 | self.textField.text! = self.formatter.string(from: dateValue) 470 | } 471 | } 472 | 473 | override func getCellData()-> (key: String, value: AnyObject){ 474 | let key = self.name! 475 | let value = self.textField.text as AnyObject 476 | return(key, value) 477 | } 478 | } 479 | 480 | open class ButtonCell: FormCell, UITextFieldDelegate { 481 | 482 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 483 | super.init(style: style, reuseIdentifier: reuseIdentifier) 484 | self.setup() 485 | } 486 | 487 | required public init?(coder aDecoder: NSCoder) { 488 | super.init(coder: aDecoder) 489 | self.setup() 490 | } 491 | 492 | override open func awakeFromNib() { 493 | super.awakeFromNib() 494 | self.setup() 495 | } 496 | 497 | override func setup() { 498 | textLabel?.textColor = tintColor 499 | selectionStyle = .default 500 | accessoryType = .none 501 | editingAccessoryType = accessoryType 502 | textLabel?.textAlignment = .center 503 | textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.title1) 504 | } 505 | } 506 | 507 | 508 | open class LinkCell : FormCell { 509 | 510 | lazy var valueLabel:UILabel = { 511 | let l = UILabel() 512 | l.translatesAutoresizingMaskIntoConstraints = false 513 | l.baselineAdjustment = .alignCenters 514 | l.textAlignment = .right 515 | l.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body) 516 | l.textColor = UIColor.black 517 | l.backgroundColor = UIColor.clear 518 | return l 519 | }() 520 | 521 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 522 | super.init(style:.value1, reuseIdentifier: reuseIdentifier) 523 | self.setup() 524 | textLabel?.textColor = .black 525 | } 526 | 527 | required public init?(coder aDecoder: NSCoder) { 528 | super.init(coder: aDecoder) 529 | self.setup() 530 | } 531 | 532 | override open func awakeFromNib() { 533 | super.awakeFromNib() 534 | self.setup() 535 | } 536 | 537 | override func setup() { 538 | super.setup() 539 | accessoryType = .disclosureIndicator 540 | editingAccessoryType = .none 541 | addSubview(valueLabel) 542 | } 543 | 544 | open override func updateConstraints() { 545 | super.updateConstraints() 546 | let margins = self.layoutMarginsGuide 547 | valueLabel.heightAnchor.constraint(equalTo: margins.heightAnchor, multiplier: 0.8).isActive = true 548 | valueLabel.centerYAnchor.constraint(equalTo: margins.centerYAnchor).isActive = true 549 | valueLabel.rightAnchor.constraint(equalTo: margins.rightAnchor, constant:-40).isActive = true 550 | valueLabel.widthAnchor.constraint(equalTo: margins.widthAnchor, multiplier: 0.5).isActive = true 551 | } 552 | 553 | open override func prepareForReuse() { 554 | super.prepareForReuse() 555 | valueLabel.text = "" 556 | } 557 | 558 | override func setCellData(key: String, value: AnyObject){ 559 | if let strValue = value as? String { 560 | valueLabel.text = strValue 561 | } 562 | } 563 | 564 | override func getCellData()-> (key: String, value: AnyObject){ 565 | let key = self.name! 566 | let value = valueLabel.text as AnyObject 567 | return(key, value) 568 | } 569 | 570 | } 571 | 572 | 573 | open class SliderCell: FormCell { 574 | 575 | lazy var slider:UISlider = { 576 | let s = UISlider() 577 | s.minimumValue = 0 578 | s.maximumValue = 100 579 | s.isContinuous = true 580 | s.tintColor = UIColor.green 581 | s.addTarget(self, action: #selector(sliderValueDidChange(_:)), for: .valueChanged) 582 | s.widthAnchor.constraint(equalToConstant: self.bounds.width*0.9).isActive = true 583 | return s 584 | }() 585 | 586 | lazy var titleLabel:UILabel = { 587 | let l = self.getLabel() 588 | return l 589 | }() 590 | 591 | lazy var minLabel:UILabel = { 592 | let l = self.getLabel() 593 | return l 594 | }() 595 | 596 | lazy var maxLabel:UILabel = { 597 | let l = self.getLabel() 598 | return l 599 | }() 600 | 601 | lazy var valueLabel:UILabel = { 602 | let l = self.getLabel() 603 | l.textColor = UIColor.lightGray 604 | l.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.subheadline) 605 | l.text = "00 %" 606 | return l 607 | }() 608 | 609 | lazy var sliderStack:UIStackView = { 610 | let s = UIStackView() 611 | s.axis = .horizontal 612 | s.distribution = .fillProportionally 613 | s.alignment = .fill 614 | s.spacing = 0 615 | s.autoresizingMask = [.flexibleWidth, .flexibleHeight] 616 | s.addArrangedSubview(UIView()) 617 | s.addArrangedSubview(self.minLabel) 618 | s.addArrangedSubview(UIView()) 619 | s.addArrangedSubview(self.slider) 620 | s.addArrangedSubview(UIView()) 621 | s.addArrangedSubview(self.maxLabel) 622 | s.addArrangedSubview(UIView()) 623 | return s 624 | }() 625 | 626 | 627 | lazy var mainStack:UIStackView = { 628 | print("main") 629 | let s = UIStackView(frame: self.bounds) 630 | s.axis = .vertical 631 | s.distribution = .fill 632 | s.alignment = .fill 633 | s.spacing = 0 634 | s.autoresizingMask = [.flexibleWidth, .flexibleHeight] 635 | s.addArrangedSubview(self.titleLabel) 636 | s.addArrangedSubview(self.sliderStack) 637 | s.addArrangedSubview(self.valueLabel) 638 | return s 639 | }() 640 | 641 | override func setup() { 642 | self.textLabel?.textColor = .clear 643 | self.addSubview(self.mainStack) 644 | } 645 | 646 | override open func layoutSubviews() { 647 | super.layoutSubviews() 648 | self.titleLabel.text = self.textLabel?.text 649 | self.minLabel.text = " \(String(Int(self.slider.minimumValue))) % " 650 | self.maxLabel.text = " \(String(Int(self.slider.maximumValue))) %" 651 | } 652 | 653 | 654 | @objc func sliderValueDidChange(_ sender:UISlider!){ 655 | self.valueLabel.text = "\(Int(sender.value)) % " 656 | } 657 | 658 | 659 | override func setCellData(key: String, value: AnyObject){ 660 | if let v = value.floatValue { 661 | self.slider.value = v 662 | self.valueLabel.text = "\(Int(v)) % " 663 | } 664 | } 665 | 666 | override func getCellData()-> (key: String, value: AnyObject){ 667 | let key = self.name! 668 | let value = Int(self.slider.value) as AnyObject 669 | return(key, value) 670 | } 671 | 672 | func getLabel()->UILabel{ 673 | let l = UILabel() 674 | l.textAlignment = .center 675 | l.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.caption1) 676 | l.textColor = UIColor.black 677 | l.backgroundColor = UIColor.clear 678 | return l 679 | } 680 | } 681 | 682 | open class SwitchCell:FormCell { 683 | 684 | lazy var switcher:UISwitch = { 685 | let s = UISwitch() 686 | s.addTarget(self, action: #selector(switchDidChange(_:)), for: .valueChanged) 687 | return s 688 | }() 689 | 690 | @objc func switchDidChange(_ sender:UISwitch!){ 691 | 692 | } 693 | 694 | lazy var switchStack:UIStackView = { 695 | let s = UIStackView(frame: self.bounds) 696 | s.axis = .horizontal 697 | s.distribution = .fill 698 | s.alignment = .center 699 | s.spacing = 5 700 | s.autoresizingMask = [.flexibleWidth, .flexibleHeight] 701 | s.addArrangedSubview(UIView()) 702 | s.addArrangedSubview(self.switcher) 703 | s.addArrangedSubview(self.space) 704 | return s 705 | }() 706 | 707 | 708 | override func setup() { 709 | self.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.caption1) 710 | self.addSubview(self.switchStack) 711 | } 712 | 713 | override func setCellData(key: String, value: AnyObject){ 714 | let boolValue = value as! Bool 715 | self.switcher.isOn = boolValue 716 | } 717 | 718 | override func getCellData()-> (key: String, value: AnyObject){ 719 | let key = self.name! 720 | let value = self.switcher.isOn as AnyObject 721 | return(key, value) 722 | } 723 | } 724 | 725 | open class StepperCell:FormCell { 726 | 727 | lazy var stepper:UIStepper = { 728 | let s = UIStepper() 729 | s.addTarget(self, action: #selector(stepperDidChange(_:)), for: .valueChanged) 730 | return s 731 | }() 732 | 733 | lazy var valueLabel:UILabel = { 734 | let l = UILabel() 735 | l.textAlignment = .center 736 | l.textColor = UIColor.black 737 | l.backgroundColor = UIColor.clear 738 | return l 739 | }() 740 | 741 | @objc func stepperDidChange(_ sender:UIStepper!){ 742 | self.valueLabel.text = "\(Int(sender.value))" 743 | } 744 | 745 | lazy var switchStack:UIStackView = { 746 | let s = UIStackView(frame: self.bounds) 747 | s.axis = .horizontal 748 | s.distribution = .fill 749 | s.alignment = .center 750 | s.spacing = 5 751 | s.autoresizingMask = [.flexibleWidth, .flexibleHeight] 752 | s.addArrangedSubview(UIView()) 753 | s.addArrangedSubview(self.valueLabel) 754 | s.addArrangedSubview(self.stepper) 755 | s.addArrangedSubview(self.space) 756 | return s 757 | }() 758 | 759 | override func setup() { 760 | self.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.caption1) 761 | self.addSubview(self.switchStack) 762 | } 763 | 764 | override func setCellData(key: String, value: AnyObject){ 765 | if let dblValue = value.int64Value { 766 | self.valueLabel.text = "\(Int(dblValue))" 767 | self.stepper.value = Double(dblValue) 768 | } 769 | } 770 | 771 | override func getCellData()-> (key: String, value: AnyObject){ 772 | let key = self.name! 773 | let value = self.stepper.value as AnyObject 774 | return(key, value) 775 | } 776 | } 777 | 778 | extension UIView{ 779 | func addConstraintsWithFormat(_ format:String, views:UIView...){ 780 | var viewsDictionary = [String:UIView]() 781 | for (index, view) in views.enumerated(){ 782 | let key = "v\(index)" 783 | viewsDictionary[key]=view 784 | } 785 | addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: .alignAllLastBaseline, metrics: nil, views: viewsDictionary)) 786 | } 787 | } 788 | 789 | --------------------------------------------------------------------------------