Eureka Form in UITableView programmatically? - ios

I am using Eureka Forms in my Swift 4 app and I do not use storyboards. Setting up the form programmatically is working fine, however I am using a popoverPresentationController to display a UIViewController with a NavBar in and using the form as a FormViewController means the top of the form is hidden underneath the NavBar.
I'd like make the Class as UIViewController and then add the Eureka form as a Subview so I can using constraints as normal. I understand this is achieved by using UITableView, so I have set it up as follows but I'm not sure how to wire in the Eureka Form and I can't find anything in the documentation about it.
import UIKit
import Eureka
class TestForm: UIViewController, UITableViewDelegate {
let navBar: UINavigationBar = {
let nav = UINavigationBar(frame: .zero)
nav.translatesAutoresizingMaskIntoConstraints = false
return nav
}()
let formTable: UITableView = {
let table = UITableView(frame: .zero, style: .grouped)
table.translatesAutoresizingMaskIntoConstraints = false
table.backgroundColor = .red
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(navBar)
view.addSubview(formTable)
formTable.delegate = self
navBar.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
navBar.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
navBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
navBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
formTable.translatesAutoresizingMaskIntoConstraints = false
formTable.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
formTable.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
formTable.topAnchor.constraint(equalTo: navBar.bottomAnchor).isActive = true
formTable.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
let navItem = UINavigationItem(title: "Add Event")
let cancelItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.cancel, target: nil, action: #selector(dismissView))
navItem.leftBarButtonItem = cancelItem
navBar.setItems([navItem], animated: false)
Form +++ Section("First Section")
<<< TextRow("Section 1 Text"){ row in
row.title = "Text Row"
row.placeholder = "Enter text here"
}
<<< PhoneRow("Section 1 Phone"){
$0.title = "Phone Row"
$0.placeholder = "And numbers here"
}
.onCellSelection({ (cell, row) in
self.saveForm()
})
}
}
Any pointers?

Related

Disable swipe down to dismiss gesture in page sheet modal with tableview

Similar question to :
Disable gesture to pull down form/page sheet modal presentation
Looking for functionality exactly like this, except with a tableview under the navigation controller:
when I attempt to use the answer given, touchesBegan and touchesEnded do not get called, so I tried calling the functions in scrollViewWillBeginDragging and scrollViewWillEndDragging and while it does get called, it doesnt solve the issue. Scrolling the tableview will still close the modal most of the time.
I do not want to present over full screen.
isModalInPresentation stops it from closing but the bounce down still attempts. I want to stop that altogether.
I want to be able to only close if the user presses cancel or actually grabs the navigation bar to close like in the gif, and swipes within the tableview to only scroll the tableview. What else can I try?
If I understand well, this is an example to do it programmatically... Declare button under your controller class:
let myButton: UIButton = {
let b = UIButton()
b.backgroundColor = .black
b.setTitle("Tap me!", for: .normal)
b.setTitleColor(.white, for: .normal)
b.titleLabel?.font = .systemFont(ofSize: 17, weight: .regular)
b.layer.cornerRadius = 12
b.clipsToBounds = true
b.translatesAutoresizingMaskIntoConstraints = false
return b
}()
Now in viewDidLoad add target and set constraints:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
myButton.addTarget(self, action: #selector(callSeetController), for: .touchUpInside)
view.addSubview(myButton)
myButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
myButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
myButton.widthAnchor.constraint(equalToConstant: 200).isActive = true
}
After that call the func to present an set sheet attributes:
#objc fileprivate func callSeetController() {
let detailViewController = SecondController()
let nav = UINavigationController(rootViewController: detailViewController)
nav.isModalInPresentation = true // disable swipe down
if let sheet = nav.sheetPresentationController {
sheet.detents = [.large()]
}
let image = UIImage(systemName: "x.circle")
let dismiss = UIBarButtonItem(image: image, primaryAction: .init(handler: { [weak self] _ in
if let sheet = nav.sheetPresentationController {
sheet.animateChanges {
self?.dismiss(animated: true, completion: nil)
}
}
}))
detailViewController.navigationItem.rightBarButtonItem = dismiss
detailViewController.navigationController?.navigationBar.tintColor = .white
present(nav, animated: true, completion: nil)
}
The result:

Passing a value to a previous view controller doesn't reflect the change

I have MainViewController and DetailViewController that are stacked together by a navigation controller. I want to pass a value from DetailViewController back to the previous controller, which is MainViewController.
First, I tried it with UINavigationControllerDelegate:
class DetailViewController: UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
(viewController as? MainViewController)?.myClass = myClass
}
}
which was to be called as DetailViewController is popped:
_ = navigationController?.popViewController(animated: true)
But, the new value doesn't get reflected on MainViewController:
class MainViewController: UIViewController {
var myClass: MyClass
private lazy var commentLabel: UILabel = {
let comment = UILabel()
comment.text = myClass.comment
comment.numberOfLines = 0
return comment
}()
}
even though when I log myClass in MainViewController, I can see that it's being passed properly.
I also tried it with a property observer so that DetailViewController can pass it to a temporary property observer instead:
var temp: MyClass? {
willSet(newValue) {
myClass = newValue
}
}
but, the view controller's interface still doesn't change.
Finally, I tried creating a delegate in MainViewController:
protocol CallBackDelegate {
func callBack(value: MyClass)
}
where the function simply passes the argument:
func callBack(value: MyClass) {
myClass = value
}
I set the delegate to self:
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
and invoking the function in DetailViewController:
delegate?.callBack(value: MyClass)
but, still doesn't update the interface. It seems as though passing the value isn't the issue, but having it be reflected is.
This is an example of using the protocol / delegate pattern. It's about as basic as it gets...
Start a new single-view project
add the code below
Set the class of the default view controller to MainViewController
embed it in a Navigation Controller
run the app
Then:
Tap the button labeled "Push to next VC"
Enter some text in the "Edit Me" field
Tap the "Pop back to previous VC"
See that the label has been updated with your entered text.
protocol CallBackDelegate: class {
func callback(_ val: String)
}
class MainViewController: UIViewController, CallBackDelegate {
let btn = UIButton()
let theLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
btn.translatesAutoresizingMaskIntoConstraints = false
theLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(theLabel)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
theLabel.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0),
theLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
theLabel.backgroundColor = .yellow
btn.backgroundColor = .red
theLabel.text = "Default text"
btn.setTitle("Push to next VC", for: [])
btn.addTarget(self, action: #selector(self.pushButtonTapped(_:)), for: .touchUpInside)
}
#objc func pushButtonTapped(_ sender: Any?) -> Void {
let vc = DetailViewController()
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
func callback(_ val: String) {
theLabel.text = val
}
}
class DetailViewController: UIViewController {
weak var delegate: CallBackDelegate?
let textField = UITextField()
let btn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
btn.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(textField)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textField.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
textField.centerXAnchor.constraint(equalTo: g.centerXAnchor),
textField.widthAnchor.constraint(equalToConstant: 200.0),
btn.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
textField.backgroundColor = .yellow
textField.borderStyle = .roundedRect
btn.backgroundColor = .blue
textField.placeholder = "Edit me"
btn.setTitle("Pop back to previous VC", for: [])
btn.addTarget(self, action: #selector(self.popButtonTapped(_:)), for: .touchUpInside)
}
#objc func popButtonTapped(_ sender: Any?) -> Void {
if let s = textField.text {
delegate?.callback(s)
}
self.navigationController?.popViewController(animated: true)
}
}
Doesn't seem that you are updating the UILabel value anyhow
var myClass: MyClass? {
didSet {
self.commentLabel.text = myClass?.comment
}
}
You have to update the label text itself, right now it's constant with the first load data

FSCalendar content getting squished when I switch scope to month

My FSCalendar's content shrinks when I switch its scope from week to month if there is a view that's constrained to its bottom anchor.
Here is a quick gif to show what exactly is happening
I have tried everything at this point. Using calendar.setScope() instead of calendar.scope =, constraining attachedToCalendarView.topAnchor to calendar.bottomAnchor calendar.contentView.bottomAnchor, and calendar.daysContainer.bottomAnchor, even turning attachedToCalendarView 's constraints on and off depending on whether it's week scope or scope month.
Not sure what else to try. Here is the code:
import UIKit
import FSCalendar
class TestController : UIViewController, FSCalendarDataSource, FSCalendarDelegate, FSCalendarDelegateAppearance {
fileprivate weak var calendar: FSCalendar!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setUp()
}
#objc func switchCalendarScope(){
if self.calendar.scope == FSCalendarScope.month {
self.calendar.scope = FSCalendarScope.week
} else {
self.calendar.scope = FSCalendarScope.month
}
}
func setUp(){
let calendar = FSCalendar()
calendar.dataSource = self
calendar.delegate = self
self.calendar = calendar
self.calendar.scope = .week
self.calendar.locale = Locale(identifier: "en_EN")
self.calendar.calendarHeaderView.calendar.locale = Locale(identifier: "en_EN")
self.calendar.adjustsBoundingRectWhenChangingMonths = true
let testingView = UIView()
testingView.backgroundColor = .red
let attachedToCalendarView = UIView()
attachedToCalendarView.backgroundColor = .blue
view.addSubview(calendar)
view.addSubview(testingView)
view.addSubview(attachedToCalendarView)
self.calendar.translatesAutoresizingMaskIntoConstraints = false
self.calendar.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
self.calendar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
self.calendar.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
self.calendar.heightAnchor.constraint(equalToConstant: 300).isActive = true
testingView.translatesAutoresizingMaskIntoConstraints = false
testingView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
testingView.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
testingView.heightAnchor.constraint(equalToConstant: 20).isActive = true
attachedToCalendarView.translatesAutoresizingMaskIntoConstraints = false
attachedToCalendarView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// Attaching this view's topAnchor to the calendar's bottom anchor
attachedToCalendarView.topAnchor.constraint(equalTo: self.calendar.contentView.bottomAnchor).isActive = true
attachedToCalendarView.widthAnchor.constraint(equalToConstant: view.bounds.size.width).isActive = true
attachedToCalendarView.heightAnchor.constraint(equalToConstant: 20).isActive = true
// Title and button to toggle the calendar scope
self.navigationItem.title = "Test"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Toggle", style: .done, target: self, action: #selector(switchCalendarScope))
}
}
Well, I couldn't figure out how to fix the problem itself but I did find a workaround. I placed the calendar inside of an empty container view (just a simple UIView) and attached attachedToCalendarView to the container's bottomAnchor instead of the calendar's itself.
Do note however that using setScope, which animates the transition, still causes the same issue. For it to work you have to set it manually like calendar.scope = x
example:
#objc func switchCalendarScope(){
if self.calendar.scope == FSCalendarScope.month {
// self.calendar.setScope(FSCalendarScope.week, animated: true) // this will cause the calendar to be squished again
self.calendar.scope = .week
movingConstraint.constant = view.safeAreaLayoutGuide.layoutFrame.size.height * -0.20
} else {
// self.calendar.setScope(FSCalendarScope.month, animated: true)
self.calendar.scope = .month
movingConstraint.constant = 0
}
}

UISearchBar in Navigation Bar cannot enter text

I'm having a weird problem, when my app first loads you cannot enter text in the search bar no matter how many times you tap it, the search bar is nested in the navigation bar.
My app also use a tab bar, and when you switch tabs then go back to the tab with the search bar it allows you enter text... any ideas what's causing this?
Heres the code for the searchBar:
func setupSearchBar(){
let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable") as! LocationSearchTableViewController
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable
searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Location"
searchBar.isTranslucent = true
searchBar.isUserInteractionEnabled = true
for subView in searchBar.subviews{
for subsubView in subView.subviews{
if let textField = subsubView as? UITextField{
var currentTextFieldBounds = textField.bounds
currentTextFieldBounds.size.height = 40.0
textField.bounds = currentTextFieldBounds
textField.borderStyle = UITextBorderStyle.none
textField.textAlignment = NSTextAlignment.left
textField.font = UIFont(name: "System", size: 25.0)
textField.textColor = theme?.textColour
}
}
}
self.navigationController?.navigationBar.setBarColour(colour: (theme?.tabBarColour)!, tint: (theme?.textColour)!)
navigationItem.titleView = resultSearchController?.searchBar
navigationItem.titleView?.bringSubview(toFront: (resultSearchController?.searchBar)!)
searchBar.delegate = self
searchBar.showsSearchResultsButton = true
searchBar.setImage(#imageLiteral(resourceName: "location_icon.png"), for: UISearchBarIcon.resultsList, state: UIControlState.normal)
resultSearchController?.hidesNavigationBarDuringPresentation = false
resultSearchController?.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
locationSearchTable.mapView = mapView
locationSearchTable.handleMapSearchDelegate = self
}
Ok after a lot messing around, I discovered that in my custom UITabBarController I had used override func viewWillAppear(_ animated: Bool) without adding super.viewWillAppear() and that caused the problem! I assume because of that subviews weren't being laid out correctly. Hope that helps anyone who has a similar problem to mine.

Programmatically add a UISearchController to a UITableView header in swift 2

In the previous version of my view controller (based on storyboard) the search bar was working good.
In the 100% programmatically defined current version (I removed all storyboard references) the search bar has disappeared.
I am trying to understand where the problem lies, without success. Any idea?
Here is the faulty code snippet:
let resultSearchController = UISearchController()
override public func viewDidLoad() {
super.viewDidLoad()
// No nav bar
navigationController?.navigationBar.hidden = true
// — Add table view
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 0).active = true
tableView.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: 0).active = true
tableView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor, constant: 0).active = true
// Add search bar as a the table header
resultSearchController.dimsBackgroundDuringPresentation = false
resultSearchController.definesPresentationContext = true
resultSearchController.hidesNavigationBarDuringPresentation = false
resultSearchController.searchResultsUpdater = self
resultSearchController.searchBar.sizeToFit()
resultSearchController.searchBar.delegate = self
tableView.tableHeaderView = resultSearchController.searchBar
/// Add refresh control
refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl.addTarget(self, action: #selector(BigHeadViewController.refreshTable), forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl)
}
I think the problem might be the first line. You have to specify the "searchResultsController" as part of the initialisation - even if it's nil (to search in place). Try
let resultSearchController = UISearchController(searchResultsController:nil)

Resources