I am using Algolia InstantSearch and the HitsTableViewController is displaying the list of data properly, however the results are not changing with each keystroke. Does anyone know how to make the results change with each letter that's typed into the search bar? I've done everything the InstantSearch documentation has written out.
Here's the code
import UIKit
import InstantSearch
struct BestBuyItem: Codable {
let name: String
}
struct BestBuyTableViewCellConfigurator: TableViewCellConfigurable {
let model: BestBuyItem
init(model: BestBuyItem, indexPath: IndexPath) {
self.model = model
}
func configure(_ cell: UITableViewCell) {
cell.textLabel?.text = model.name
}
}
typealias BestBuyHitsViewController = HitsTableViewController<BestBuyTableViewCellConfigurator>
class ViewController: UIViewController {
let searcher = SingleIndexSearcher(appID: "latency",
apiKey: "1f6fd3a6fb973cb08419fe7d288fa4db",
indexName: "bestbuy")
lazy var searchController: UISearchController = .init(searchResultsController: hitsViewController)
lazy var searchConnector: SingleIndexSearchConnector<BestBuyItem> = .init(searcher: searcher,
searchController: searchController,
hitsController: hitsViewController,
filterState: filterState)
let hitsViewController: BestBuyHitsViewController = .init()
let statsInteractor: StatsInteractor = .init()
let filterState: FilterState = .init()
lazy var categoryConnector: FacetListConnector = .init(searcher: searcher,
filterState: filterState,
attribute: "category",
operator: .or,
controller: categoryListController,
presenter: FacetListPresenter(sortBy: [.isRefined]))
lazy var categoryListController: FacetListTableController = .init(tableView: categoryTableViewController.tableView)
let categoryTableViewController: UITableViewController = .init()
override func viewDidLoad() {
super.viewDidLoad()
searchConnector.connect()
categoryConnector.connect()
statsInteractor.connectSearcher(searcher)
statsInteractor.connectController(self)
searcher.search()
setupUI()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
searchController.searchBar.becomeFirstResponder()
}
func setupUI() {
view.backgroundColor = .white
navigationItem.searchController = searchController
navigationItem.rightBarButtonItem = .init(title: "Category", style: .plain, target: self, action: #selector(showFilters))
searchController.hidesNavigationBarDuringPresentation = false
searchController.showsSearchResultsController = true
searchController.automaticallyShowsCancelButton = false
categoryTableViewController.title = "Category"
}
#objc func showFilters() {
let navigationController = UINavigationController(rootViewController: categoryTableViewController)
categoryTableViewController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissFilters))
present(navigationController, animated: true, completion: .none)
}
#objc func dismissFilters() {
categoryTableViewController.navigationController?.dismiss(animated: true, completion: .none)
}
}
extension ViewController: StatsTextController {
func setItem(_ item: String?) {
title = item
}
}
I really appreciate anyone that helps, thanks
Related
I have a class called ThemeVC which has a textview (connected with an IBoutlet) and functionalities applied to it (it has a recognizer that detects the tapped words).
My goal here is that I would like to extract that piece of functionality, and put it maybe in its own class or create a delegate so I could reuse that functionality on other textviews.
Anyone knows how?
I pasted my code below.
(HERE comments, are functions that should be called from any view controller)
import UIKit
class ThemeVC: UIViewController, UITextViewDelegate, UINavigationControllerDelegate {
#IBOutlet weak var themeTextView: UITextView!
var tB = UIBarButtonItem()
// Move away from ThemeVC ... ->
var selectionDict = [String:Int]()
var viewTagCount = Int()
var tap = UIGestureRecognizer()
var firstTimeGrouped = false
// -> ... Move away from ThemeVC
override func viewDidLoad() {
super.viewDidLoad()
themeTextView.delegate = self
loadbuttons ()
//HERE
addTagSelectorToolBar ()
}
func loadbuttons () {
tB = UIBarButtonItem(image: UIImage(systemName: "hand.point.up.left"), style: .plain, target: self, action: #selector(getTag(sender:)))
navigationItem.rightBarButtonItems = [tB]
}
#objc func getTag(sender: AnyObject) {
themeTextView.resignFirstResponder()
//HERE
startTagSelection()
}
}
// Move away from ThemeVC ... ->
extension ThemeVC {
func startTagSelection () {
navigationController?.setToolbarHidden(false, animated: false)
tap.isEnabled = true
tB.isEnabled = false
themeTextView.isEditable = false
themeTextView.isSelectable = false
}
}
extension ThemeVC {
#objc func doneTagSelection(){
navigationController?.setToolbarHidden(true, animated: false)
tap.isEnabled = false
tB.isEnabled = true
themeTextView.isEditable = true
themeTextView.isSelectable = true
firstTimeGrouped = false
}
}
extension ThemeVC {
func addTagSelectorToolBar (){
addTappedTagRecognizer()
tap.isEnabled = false
let done = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTagSelection))
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
toolbarItems = [spacer, done]
}
}
extension ThemeVC {
func addTappedTagRecognizer () {
tap = UITapGestureRecognizer(target: self, action: #selector(tapResponse(recognizer:)))
tap.delegate = self as? UIGestureRecognizerDelegate
themeTextView.addGestureRecognizer(tap)
}
#objc private func tapResponse(recognizer: UITapGestureRecognizer) {
let location: CGPoint = recognizer.location(in: themeTextView)
let position: CGPoint = CGPoint(x:location.x, y:location.y)
let tapPosition: UITextPosition? = themeTextView.closestPosition(to:position)
if tapPosition != nil {
let textRange: UITextRange? = themeTextView.tokenizer.rangeEnclosingPosition(tapPosition!, with: UITextGranularity.word, inDirection: UITextDirection(rawValue: 1))
if textRange != nil
{
let tappedWord: String? = themeTextView.text(in:textRange!)
print(tappedWord ?? "Unable to get word")
}
}
}
}
// ... -> Move away from ThemeVC
How to test my code:
Create a new project with a storyboard
On the left hand side rename viewcontroller with themeVC, and replace
its code with the code I gave.
On the storyboard, embed the controller in a navigation controller, on right side, change in identity inspector class from view controller to themeVC
add a textview and link it to the IBoutlet
Looking at the parts you want to move away from ThemeVC, I would have to say not everything should be moved away from ThemeVC.
For example, you marked startTagSelection as something you want to move away, but you reference the navigationController which belongs to the view controller so it should ideally not be the responsibility of your UITextView to update your UINavigationBar.
So the two ideas discussed in the comments was using SubClasses and Protocols.
Protocols was the suggestion of Ptit Xav so I will show one way that could be used, Ptit Xav could add an answer if something else was in mind.
I start with creating a protocol
// Name the protocol as you see appropriate
// I add #objc so it can be accessible from Storyboard
// This will be used to `hand over` responsibility of
// a certain action / event
#objc
protocol CustomTextViewTagDelegate: class {
func customTextViewDidStartSelection(_ textView: CustomTextView)
func customTextViewDidFinishSelection(_ textView: CustomTextView)
}
Next I subclass a UITextView to add my own customization
#IBDesignable
class CustomTextView: UITextView {
var selectionDict = [String:Int]()
var viewTagCount = Int()
var tap = UIGestureRecognizer()
var firstTimeGrouped = false
// Name it as you wish
// #IBInspectable added for storyboard accessibility
// You could also make it an IBOutlet if your prefer
// that interaction
#IBInspectable
weak var tagDelegate: CustomTextViewTagDelegate?
func startTagSelection () {
// Remove the commented lines as this should the responsibility of
// the view controller, manage in the view controller using the delegate
// navigationController?.setToolbarHidden(false, animated: false)
// tB.isEnabled = false
tap.isEnabled = true
isEditable = false
isSelectable = false
// Hand over responsibility of this action back whatever
// has subscribed as the delegate to implement anything else
// for this action
tagDelegate?.customTextViewDidStartSelection(self)
}
func addTappedTagRecognizer () {
tap = UITapGestureRecognizer(target: self,
action: #selector(tapResponse(recognizer:)))
tap.delegate = self as? UIGestureRecognizerDelegate
addGestureRecognizer(tap)
}
#objc private func tapResponse(recognizer: UITapGestureRecognizer) {
let location: CGPoint = recognizer.location(in: self)
let position: CGPoint = CGPoint(x:location.x,
y: location.y)
let tapPosition: UITextPosition? = closestPosition(to:position)
if tapPosition != nil {
let textRange: UITextRange? = tokenizer.rangeEnclosingPosition(tapPosition!,
with: UITextGranularity.word,
inDirection: UITextDirection(rawValue: 1))
if textRange != nil
{
let tappedWord: String? = text(in:textRange!)
print(tappedWord ?? "Unable to get word")
}
}
}
#objc func doneTagSelection() {
// This is not the text view's responsibility, manage in the
// view controller using the delegate
// navigationController?.setToolbarHidden(true, animated: false)
// tB.isEnabled = true
tap.isEnabled = false
isEditable = true
isSelectable = true
firstTimeGrouped = false
// Hand over responsibility of this action back whatever
// has subscribed as the delegate to implement anything else
// for this action
tagDelegate?.customTextViewDidFinishSelection(self)
}
}
And finally use it like so
class ThemeVC: UIViewController {
// Change UITextView to CustomTextView
#IBOutlet weak var themeTextView: CustomTextView!
var tB = UIBarButtonItem()
// If you do not set up the delegate in your
// storyboard, you need to it in your code
// call this function from didLoad or something
// if needed
private func configureTextView() {
themeTextView.tagDelegate = self
}
// All your other implementation
}
extension ThemeVC: CustomTextViewTagDelegate {
func customTextViewDidStartSelection(_ textView: CustomTextView) {
navigationController?.setToolbarHidden(false,
animated: false)
tB.isEnabled = false
}
func customTextViewDidFinishSelection(_ textView: CustomTextView) {
navigationController?.setToolbarHidden(true,
animated: false)
tB.isEnabled = true
}
}
I did not add addTagSelectorToolBar as part of the CustomTextView implementation as this is not a good candidate to be part of that module as all of its code is related to the view controller so i don't recommend making a part of the CustomTextView implementation.
I have the following code for the DatePickerController:
import UIKit
#objc protocol DatePickerControllerDelegate: AnyObject {
func datePicker(controller: DatePickerController, didSelect date: Date)
func datepickerDidDismiss(controller: DatePickerController)
}
final class DatePickerController: UIViewController {
#objc weak var delegate: DatePickerControllerDelegate?
#objc public var date: Date {
get {
return datePicker.date
}
set(value) {
datePicker.setDate(value, animated: false)
}
}
#objc public lazy var datePicker: UIDatePicker = {
let v = UIDatePicker()
v.datePickerMode = .date
return v
}()
override var preferredContentSize: CGSize {
get {
return datePicker.frame.size
}
set(newValue) {
super.preferredContentSize = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(datePicker)
datePicker.autoresizingMask = [.flexibleTopMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleBottomMargin]
view.backgroundColor = .white
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done,
target: self,
action: #selector(DatePickerController.doneButtonDidTap))
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
target: self,
action: #selector(DatePickerController.cancelButtonDidTap))
}
#objc private func doneButtonDidTap() {
delegate?.datePicker(controller: self, didSelect: date)
}
#objc private func cancelButtonDidTap() {
dismiss(animated: true, completion: nil)
delegate?.datepickerDidDismiss(controller: self)
}
}
It works well on iPad:
However, on iPhone the picker slides up under the UINavigationBar:
The code used to show the DatePickerController is absolutely the same, the style is UIModalPresentationStylePopover in both cases.
Update: code to present DatePickerController:
#objc func presentDatePicker() {
let picker = DatePickerController()
let navC = UINavigationController(rootViewController: picker)
present(navC, animated: true, completion: nil)
}
Fixed using AutoLayout:
private func configureLayout() {
datePicker.translatesAutoresizingMaskIntoConstraints = false
[datePicker.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
datePicker.leadingAnchor.constraint(equalTo: view.leadingAnchor),
datePicker.trailingAnchor.constraint(equalTo: view.trailingAnchor),]
.forEach{$0.isActive = true}
}
I've spent 20+ hours searching/trying different solutions just for this specific problem and cannot make it work. I'm just starting to learn Swift, so be gentile... I made sure to exhaust all options before asking for help.
I have 3 view controllers (VC1: form with 5 text fields, VC2: tableview for displaying gathered data, VC3: 2nd form with 3 text fields and an image picker)
screen shot of storyboard
VC1 gathers the text field data, pass to VC2, and use custom cell #1 to add to the tableview. I used the exact same methods to do the same for VC3 (changing the cell identifier, and using if/else to determine which section to add to) but cannot get any results. At first I thought my data wasn't being passed, but that checks out ("Finihed & Send" button set to alert and prints variable's text) next I thought it was my logic, but custom cell #1 works... I've been staring it this code for so long, I have horrible dreams about it. I feel like this should be obtainable with the techniques I'm applying but wonder if I am I wandering into Core Data territory but just don't know it.
tableview with only 1 custom cell
My intent is to have VC1 add a cell (custom cell 1) at indexPath.row 0 and VC3 to add (custom cell 2) indexPath.row >= 1. Everything works like I want it to with the exception of adding a second custom cell to the tableview.
addWorkViewController
import UIKit
import MapKit
import CoreLocation
class mainVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CLLocationManagerDelegate, UITextFieldDelegate {
#IBOutlet weak var scrollviewWORK: UIScrollView!
#IBOutlet weak var typeWORK: UISegmentedControl!
#IBOutlet weak var locationWORK: UITextField!
#IBOutlet weak var positionWORK: UISegmentedControl!
#IBOutlet weak var priceWORK: UITextField!
#IBOutlet weak var photo1WORK: UIImageView!
#IBOutlet weak var descriptionWORK: UITextView!
/// Prepare Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case is WorkOverview:
let workDest: WorkOverview = segue.destination as! WorkOverview
var cost = ""
var snap = UIImage()
if (priceWORK.text == nil) {
cost = ""
} else {
cost = priceWORK.text!
}
if (photo1WORK.image == nil) {
snap = UIImage()
} else {
snap = photo1WORK.image!
}
workDest.workLocation = locationWORK.text!
workDest.workDescription = descriptionWORK.text!
workDest.workPrice = cost
workDest.workPhoto = snap
case is PlantList:
let plantDest: PlantList = segue.destination as! PlantList
plantDest.placeholder = ""
default:
break
}
}
/// END Segue Preparation
/// Save to List Button
#IBAction func saveToListBTN(_ sender: UIButton) {
performSegue(withIdentifier: "unwindToList", sender: self)
}
/// END Save to List Button
/// Insert Plant
#IBAction func insertPlant(_ sender: UIButton) {
performSegue(withIdentifier: "toPlantListSegue", sender: self)
}
var addedPlant: String? = ""
/// END Insert Plant
/// Clear All Button
#IBAction func clearAllBTN(_ sender: UIButton) {
}
/// END Clear All Button
/// Segmented Controller - Work Type
#IBAction func positionChanged(_ sender: UISegmentedControl) {
switch positionWORK.selectedSegmentIndex {
case 0:
locationWORK.text? += " - Front"
case 1:
locationWORK.text? += " - Back"
case 2:
locationWORK.text? += " - Side"
default:
break
}
}
#IBAction func indexChanged(_ sender: UISegmentedControl) {
switch typeWORK.selectedSegmentIndex {
case 0:
descriptionWORK.text = "Provide and install "
case 1:
descriptionWORK.text = "Replace "
case 2:
descriptionWORK.text = "Remove and dispose of "
default:
break
}
}
/// END Segmented Controller - Work Type
/// ScrollView Keyboard Adjust
func textFieldDidBeginEditing(_ textField: UITextField) {
if (textField == priceWORK){
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 205), animated: true)
} else {}
}
func textFieldDidEndEditing(_ textField: UITextField) {
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
/// END Scrollview Keyboard Adjust
/// VIEWDIDLOAD
override func viewDidLoad() {
super.viewDidLoad()
// Toolbar
let toolBar = UIToolbar()
toolBar.sizeToFit()
let backArrow = UIBarButtonItem.init(image: #imageLiteral(resourceName: "backArrow"), style: .plain, target: nil, action: #selector(backArrowClicked))
let spacerA = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let workDoneBtn = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneBtnClicked))
let spacerB = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let nextArrow = UIBarButtonItem.init(image: #imageLiteral(resourceName: "nextArrow"), style: .plain, target: nil, action: #selector(nextArrowClicked))
toolBar.setItems([backArrow, spacerA, workDoneBtn, spacerB, nextArrow], animated: false)
toolBar.setItems([backArrow, spacerA, workDoneBtn, spacerB, nextArrow], animated: false)
locationWORK.inputAccessoryView = toolBar
priceWORK.inputAccessoryView = toolBar
descriptionWORK.inputAccessoryView = toolBar
}
/// END VIEWDIDLOAD
/// Toolbar - Done Button
#objc func doneBtnClicked() {
view.endEditing(true)
}
/// END Toolbar - Done Button
/// Arrow to Next TextField
#objc func nextArrowClicked() {
if (locationWORK.isFirstResponder) {
descriptionWORK.becomeFirstResponder()
} else if (descriptionWORK.isFirstResponder) {
priceWORK.becomeFirstResponder()
} else if (priceWORK.isFirstResponder) {
view.endEditing(true)
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
}
/// END Arrow to Next TextField
/// Arrow to Previous TextField
#objc func backArrowClicked() {
if (locationWORK.isFirstResponder) {
view.endEditing(true)
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
} else if (descriptionWORK.isFirstResponder) {
locationWORK.becomeFirstResponder()
} else if (priceWORK.isFirstResponder) {
descriptionWORK.becomeFirstResponder()
}
}
/// END Arrow to Previous TextField
/// Image Select from Library & Camera
#IBAction func takePhotoONE(_ sender: UIButton) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
let actionSheet = UIAlertController(title: "Want to add a photo?", message: "Please choose a source.", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { (action:UIAlertAction) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
}else{
print("Camera is not available")
}
}))
actionSheet.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: { (action:UIAlertAction) in imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "Remove Photo", style: .destructive, handler: { (action:UIAlertAction) in self.photo1WORK.image = nil}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
photo1WORK.image = image
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
/// END Image Select from Library & Camera
/// GPS Location
let addressManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let streetAddress = locations[0]
CLGeocoder().reverseGeocodeLocation(streetAddress) { (placemark, error) in
if error != nil
{
print ("Sorry, there has been an error.")
}
else
{
if let place = placemark?[0]
{
if place.subThoroughfare != nil
{
self.locationWORK.text = "\(place.subThoroughfare!) \(place.thoroughfare!)"
}
}
}
}
}
#IBAction func getGPS(_ sender: UIButton) {
// Address
addressManager.delegate = self
addressManager.desiredAccuracy = kCLLocationAccuracyBest
addressManager.requestWhenInUseAuthorization()
addressManager.startUpdatingLocation()
}
/// END GPS Location
}
TableViewController
import UIKit
class WorkOverview: UIViewController {
#IBOutlet weak var listTableView: UITableView!
#IBAction func addWorkBTN(_ sender: UIButton) {
performSegue(withIdentifier: "overviewToWorkSegue", sender: self)
}
#IBAction func unwindToList(segue:UIStoryboardSegue) { }
#IBAction func finishedBTN(_ sender: UIButton) {
let alertController = UIAlertController(title: "Data Pass Test", message:
workLocation, preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))
self.present(alertController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
property = createArray()
workPJs = workArray()
listTableView.delegate = self
listTableView.dataSource = self
}
var propForm = String()
var propName = String()
var propCity = String()
var propDate = String()
var propDue = String()
var propRep = String()
var workLocation = String()
var workDescription = String()
var workPrice = String()
var workPhoto = UIImage()
var workPJs: [WorkManager] = []
var property: [TaskManager] = []
func workArray() -> [WorkManager] {
var tempWork: [WorkManager] = []
let work1 = WorkManager(location: workLocation, description: workDescription, price: workPrice, photo: workPhoto)
tempWork.append(work1)
return tempWork
}
func createArray() -> [TaskManager] {
var tempProperty: [TaskManager] = []
let prop1 = TaskManager(title: propForm, property: propName, city: propCity, date: propDate, due: propDue, rep: propRep)
tempProperty.append(prop1)
return tempProperty
}
}
extension WorkOverview: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return property.count
} else {
return workPJs.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let prop = property[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "PropertyCell", for: indexPath) as! PropertyCell
cell.setProperty(prop: prop)
return cell
} else if indexPath.section == 1 {
let wrk = workPJs[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "WorkCell", for: indexPath) as! WorkCell
cell.setWork(wrk: wrk)
return cell
}
return UITableViewCell()
}
}
WorkCell class
import UIKit
class WorkCell: UITableViewCell {
#IBOutlet weak var theLocation: UILabel!
#IBOutlet weak var theDescription: UILabel!
#IBOutlet weak var thePrice: UILabel!
#IBOutlet weak var thePhoto: UIImageView!
func setWork(wrk: WorkManager) {
theLocation.text = wrk.location
theDescription.text = wrk.description
thePrice.text = wrk.price
thePhoto.image = wrk.photo
}
}
WorkManager class
import UIKit
class WorkManager {
var location: String
var description: String
var price: String
var photo: UIImage
init(location: String, description: String, price: String, photo: UIImage){
self.location = location
self.description = description
self.price = price
self.photo = photo
}
}
in ViewDidLoad add this code according to names of your cell
tableView.registerNib(UINib(nibName: "cell xib", bundle: nil), forCellReuseIdentifier: "cell name")
in your case you have to register both nib of your custom cell.
i have feelings your section if else condition is wrong. better to debug.
Here, you need to define the second TableViewCell class but in your code only WorkCell class define. so, i hope below link is help you.
UITableview with more than One Custom Cells with Swift
I changed the if statement in cellForRowAt func to a switch case (don't know if it helped or not) but I know adding .reloadData() to my tableview in the viewDidAppear func got it working. Still needs some debugging but that's life.
I have a View controller that has a tableView embedded in a ContainerView and just below the ContainerView I have a UISlider. I have code on a view controller for the UISlider and code on another view controller that controls the table view.
Properties of the UISlider are set based on the selected text field - this section of code works. I am struggling to create a function/feature that will change the textField value when the UISlider is move. I think the UISlider Action needs to on the code that controls the UISlider, but I cannot determine how to cast the value of the UISlider.setvalue between the two viewController as the slider is moved to update the textField located in a tableCell. Hopefully makes some sense.
// UISlider ViewController
override func viewDidLoad() {
super.viewDidLoad()
sliderOutlet.isContinuous = true
sliderOutlet.tintColor = UIColor.green
self.refreshSlider()
}
#objc func refreshSlider() {
sliderOutlet.minimumValue = Float(GlobalSliderValues.minimumValue)
sliderOutlet.maximumValue = Float(GlobalSliderValues.maximumValue)
sliderOutlet.value = Float(GlobalSliderValues.sliderValue)
// if let chgIntValue = Int(GlobalSliderValues.changeValue)
// { sliderOutlet.setValue(Float(Double(chgIntValue)), animated: true)
// }
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshSlider), name: Notification.Name("refreshSlider"), object: nil)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: Notification.Name("refreshSlider"), object: nil)
}
TableView Controller
override func viewDidLoad() {
super.viewDidLoad()
mortgageAmount.addTarget(self, action: #selector(chgTextFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
}
#objc func chgTextFieldDidChange(textField: UITextField)
{
if let chgStringValue = mortgageAmount.text
{
if Double(chgStringValue) ?? 1 > 10000 {
let alert = UIAlertController(title: "Input Error", message: "Value cannot be greater than 10000", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
GlobalSliderValues.minimumValue = 10
GlobalSliderValues.maximumValue = 10000
GlobalSliderValues.sliderValue = Int(mortgageAmount.text!)!
GlobalSliderValues.mortageAmountValue = Float(Int(mortgageAmount.text!)!)
NotificationCenter.default.post(name:Notification.Name("refreshSlider"), object: nil)
if let chgIntValue = Int(chgStringValue)
{ GlobalSliderValues.changeValue.setValue(Float(Double(chgIntValue)), animated: true)
}
}
}
#IBAction func valueChanged(_ sender: UISlider) {
mortgageAmount.text = String(format: "%.2f",Double(sender.value))
}
struct GlobalSliderValues {
static var minimumValue = Int()
static var maximumValue = Int()
static var lowerValue = Int()
static var UpperValue = Int()
static var locationValue = Int()
static var sliderValue = Int()
static var sliderChgValue = ""
}
We don't need notification center to deal with refreshing the slider. Since we are using constants to store value your code can be changed as follows
class ViewController: UIViewController {
#IBOutlet weak var sliderOutlet: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
sliderOutlet.isContinuous = true
sliderOutlet.tintColor = UIColor.green
}
#objc func refreshSlider() {
DispatchQueue.main.async {
self.sliderOutlet.minimumValue = Float(GlobalSliderValues.minimumValue)
self.sliderOutlet.maximumValue = Float(GlobalSliderValues.maximumValue)
self.sliderOutlet.value = Float(GlobalSliderValues.sliderValue)
print(self.sliderOutlet.minimumValue)
print(self.sliderOutlet.maximumValue)
print(self.sliderOutlet.value)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.refreshSlider()
}
}
I entered mortgage amount as so 1098 so slider look like below
I have a selector on my UIBarButton referencing a function to segue to another view controller but the function never gets called when clicked on. Through testing breakpoints I can see the function, segueToCartViewController, never gets called.
Thanks in advance!
UIBarButtonItem init
private let reuseIdentifier = "ItemCell"
private let SegueCartIdentifier = "CatalogToCart"
final class CatalogViewController: UICollectionViewController {
//MARK: -properties
var brand: Brand!
var cart: [Item]!
fileprivate let itemsPerRow:CGFloat = 3
fileprivate let sectionInsets = UIEdgeInsets(top: 30, left: 20, bottom: 30, right: 20)
private var cartItem: UIBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Cart"), style: .plain, target: self, action: #selector(segueToCartViewController(_:)))
var selectedItemIndexPath: IndexPath?{
didSet{
var indexPaths = [IndexPath]()
if let selectedItemIndexPath = selectedItemIndexPath{
indexPaths.append(selectedItemIndexPath)
}
if let oldValue = oldValue{
indexPaths.append(oldValue)
}
collectionView?.performBatchUpdates({
self.collectionView?.reloadItems(at: indexPaths)
}) { completed in
if let selectedItemIndexPath = self.selectedItemIndexPath{
self.collectionView?.scrollToItem(at: selectedItemIndexPath, at: .centeredVertically, animated: true)
}
}
}
}
override func viewDidAppear(_ animated: Bool) {
self.navigationController?.setToolbarHidden(false, animated: false)
let flex = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
self.navigationController?.toolbar.items = [flex,cartItem,flex]
}
}
call for segue
//MARK: CartNavigation
extension CatalogViewController: CartDelegate{
func segueToCartViewController(_ sender: AnyObject){
super.performSegue(withIdentifier: SegueCartIdentifier, sender: sender)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let destination = segue.destination as? UINavigationController else{
return
}
cartVC.delegate = self
}
func closeModallyPresentedViewController() {
dismiss(animated: true, completion: nil)
}
}
The target of your UIBarButtonItem is nil because self is nil during it's initialization.
You can initialize it like this instead
final class CatalogViewController: UICollectionViewController {
lazy final private var cartItem: UIBarButtonItem = { [unowned self] in
return UIBarButtonItem(image: #imageLiteral(resourceName: <#T##String#>), style: .plain, target: self, action: #selector(segueToCartViewController(_:)))
}()
override function viewDidAppear(_ animated: Bool) {
//blah blah, the rest of your code
}
}
See here for a good explanation about the value of self during initialization of properties.