I've searched the site and the only threads I've found around this subject remain unanswered so I hoped I would have more luck.
I am relatively sure what I am trying to do is fairly easy however I can't seem to find the answer.
I have a view controller with a Uitext field which when the text field is clicked it presents a Search controller modally.
Here is the code for the text field which is SetAlertTableController.swift:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
if indexPath.section == 0 {
cell.textLabel!.text = "Set a Station Alert"
cell.backgroundView = UIImageView(image: UIImage(named: "red-full"))
cell.textLabel?.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.0)
cell.imageView!.image = UIImage(named: "metro-no-pad")
cell.textLabel?.textColor = UIColor.whiteColor()
var searchField = UITextField(frame: CGRectMake(60.0, 25.0, 250.0, 30.0))
searchField.backgroundColor = UIColor.whiteColor()
searchField.borderStyle = UITextBorderStyle.Line
searchField.borderStyle = .RoundedRect
searchField.placeholder = "Search Stations"
searchField.textColor = UIColor.lightGrayColor()
searchField.delegate = self
self.view.addSubview(searchField)
} else if indexPath.section == 1 {
cell.backgroundView = UIImageView(image: UIImage(named: "red-full"))
cell.userInteractionEnabled = false
var slider=UISlider(frame:CGRectMake(10, 120, 300, 10));
slider.minimumValue = 0;
slider.maximumValue = 5;
slider.continuous = false;
slider.value = 0;
slider.addTarget(self, action: "sliderValueDidChange:", forControlEvents: .ValueChanged);
self.view.addSubview(slider);
} else if indexPath.section == 2 {
cell.textLabel!.text = "Set Alert"
cell.backgroundView = UIImageView(image: UIImage(named: "red-full"))
cell.textLabel?.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.0)
cell.imageView!.image = UIImage(named: "confirm")
cell.textLabel?.textColor = UIColor.whiteColor()
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
}
return cell
}
When you search it filters the results in to table view cells. When the correct results cell is pressed I need to dismiss the modal view and show the data from the selected cell in the textfield.
Here is the code which for the search controller (ViewController.swift) as it stands:
import UIKit
import Foundation
class ViewController: UIViewController {
let kCellIdentifier = "Cell"
var searchController: UISearchDisplayController!
var tableView: UITableView!
var tableData: [String]? {
didSet {
self.tableView.reloadData()
}
}
var tableDataFiltered = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView = UITableView(frame: CGRectZero, style: .Plain)
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: kCellIdentifier)
tableView.delegate = self
tableView.dataSource = self
self.view.addSubview(tableView)
let searchBar = UISearchBar()
searchBar.sizeToFit()
searchBar.becomeFirstResponder()
//tableView.tableHeaderView = searchBar
self.navigationItem.titleView = searchBar;
searchController = UISearchDisplayController(searchBar: searchBar, contentsController: self)
searchController.searchResultsDataSource = self
searchController.searchResultsDelegate = self
searchController.searchResultsTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: kCellIdentifier)
tableData = ["Bournemouth", "Branksome", "Parkstone"]
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "dismissView")
self.navigationItem.rightBarButtonItem = cancelButton
}
func dismissView() {
dismissViewControllerAnimated(true, completion: nil)
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
println(searchBar.text)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.frame = self.view.bounds
}
}
extension ViewController: UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView != self.tableView {
return self.tableDataFiltered.count
}
if let count = tableData?.count {
return count
}
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier, forIndexPath: indexPath) as! UITableViewCell
var data: String
if tableView != self.tableView {
data = tableDataFiltered[indexPath.row]
} else {
data = tableData![indexPath.row]
}
cell.textLabel?.text = data
return cell
}
}
extension ViewController: UITableViewDelegate {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let data = tableData?[indexPath.row] {
dismissViewControllerAnimated(true, completion: nil)
println(indexPath.row)
}
}
}
Everything works as it should at this stage, even dismissing the modal view when clicked, I just can't work out a way to pass the result back to the previous controller's text field. Any help at all is greatly appreciated.
What is the best or more practical way to do this without storyboards?
People that are claiming the question to be a duplicate of previous posts and on the verge of this post being removed, all of other posts are either in Objective-C not Swift or they rely heavily on storyboard and segue use.
Unfortunately you can't use segues without a storyboard.
There's multiple ways to do this, the one which I would recommend (As you have experience with delegates having used UITableViewController) would be to create a swift protocol which acts as the delegate on the UISearchController which would look something like this:
import UIKit
protocol ViewControllerDelegate {
func searchViewControllerDidSelectResult(searchVC:ViewController,result: String)
}
class ViewController: UIViewController {
var delegate: ViewControllerDelegate?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let data = tableData?[indexPath.row] {
if let delegate = self.delegate {
delegate.searchViewControllerDidSelectResult(self, result: data)
}
// dismissViewControllerAnimated(true, completion: nil) // Personally I would dismiss the search view controller within the delegate method of the view controller with the text field
println(indexPath.row)
}
}
}
The view controller with the text field would then look something like this. Your SetAlertTableViewController then needs to conform to ViewControllerDelegate like so:
class SetAlertTableViewController: UITableViewController, ViewControllerDelegate
And also implement the method declared in the protocol to set the text in the text field:
func searchViewControllerDidSelectResult(searchVC:ViewController, result: String) {
// Keep track of the result
self.result = result
self.tableView.reloadData()
self.dismissViewControllerAnimated(true, completion: nil)
}
You'll also need to make sure that when you show the ViewController for your search results you set the delegate on it so it will know what it's delegate is! If you need any more help, let me know!
It is hard to break oneself from the used conventions (including myself).
Rather than writing
if let delegate = self.delegate {
delegate.searchViewControllerDidSelectResult(self, result: data)
}
It would be better to go with the following:
delegate?.searchViewControllerDidSelectResult(self, result: data)
As it is not permitting me to comment, I am just providing an update (Swift is all about simplicity and elegance). It took couple of weeks for me to write in the said way.
Related
I use a custom search controller that has a tableView for showing the results,
the problem is when tapping the cancel button multiple times it dismisses the tabBarController.
But if i press it normally it acts as it should be.
class UICustomSearchController: UISearchController {
private var suggestedTableView: UITableView!
weak var suggestionDelegate: SearchSuggestionsDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
suggestedTableView = UITableView(frame: CGRect(x: 0, y: searchBar.frame.maxY,
width: view.frame.width,
height: view.frame.height - 70))
suggestedTableView.backgroundColor = UIColor.clear
suggestedTableView.tableFooterView = UIView()
view.subviews.forEach {
if $0.isKind(of: UITableView.self) {
$0.removeFromSuperview()
}
}
view.addSubview(suggestedTableView)
suggestedTableView.dataSource = self
suggestedTableView.delegate = self
suggestedTableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
let tap = UITapGestureRecognizer(target: self, action: #selector(tableTapped))
suggestedTableView.addGestureRecognizer(tap)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
suggestedTableView.removeFromSuperview()
suggestedTableView = nil
dismiss(animated: false, completion: nil)
}
func reloadSuggestions() {
suggestedTableView.reloadData()
}
private func dismissView() {
searchBar.text = ""
suggestedTableView.removeFromSuperview()
dismiss(animated: false, completion: nil)
suggestionDelegate?.didDismissed()
}
// MARK: - Actions
#objc func tableTapped(tap:UITapGestureRecognizer) {
suggestedTableView.removeGestureRecognizer(tap)
let location = tap.location(in: suggestedTableView)
let path = suggestedTableView.indexPathForRow(at: location)
if let indexPathForRow = path {
tableView(suggestedTableView, didSelectRowAt: indexPathForRow)
} else {
dismissView()
}
}
}
extension UICustomSearchController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return suggestionDelegate?.suggestions().count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let list = suggestionDelegate?.suggestions() ?? []
cell.textLabel?.text = list[indexPath.row]
cell.textLabel?.textColor = UIColor.color(from: .blueTabBar)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let list = suggestionDelegate?.suggestions() ?? []
searchBar.text = list[indexPath.row]
searchBar.becomeFirstResponder()
suggestionDelegate?.didSelect(suggestion: list[indexPath.row])
}
}
And in the viewController that has search:
func initSearchViews() {
// Create the search controller and specify that it should present its results in this same view
searchController = UICustomSearchController(searchResultsController: nil)
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.barTintColor = UIColor.white
searchController.searchBar.tintColor = UIColor.color(from: .blueTabBar)
searchController.searchBar.setValue(Strings.cancel.localized, forKey:"_cancelButtonText")
searchController.suggestionDelegate = self
if let searchTextField = searchController!.searchBar.value(forKey: "searchField") as? UITextField {
searchTextField.placeholder = Strings.search.localized
}
// Make this class the delegate and present the search
searchController.searchBar.delegate = self
}
I tried putting this line in viewController but nothing happened:
definesPresentationContext = true
my problem: I want to open some kind of Profil if a user pushes a Button in a Table-View Cell. The Cells Data is downloaded from Parse.
The idea is based on Instagram, if you click on the username-button on Insta the profile from the user who posted the image will open. I want to create the same code, but i can't create the code to get the user. Can you help me?
Heres some code:
import UIKit
import Parse
class HomeController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let reuseIdentifer = "FeedCell"
var delegate: HomeControllerDelegate?
var newCenterController: UIViewController!
let tableView = UITableView()
//Für Parse:
var users = [String: String]()
var comments = [String]()
var usernames = [String]()
var lastnames = [String]()
var imageFiles = [PFFileObject]()
var wischen: UISwipeGestureRecognizer!
var wischen2: UISwipeGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
getData()
configureNavigationBar()
configurateTableView()
wischen = UISwipeGestureRecognizer()
wischen.addTarget(self, action: #selector(handleMenuToggle))
wischen.direction = .right
wischen.numberOfTouchesRequired = 1
view.addGestureRecognizer(wischen)
wischen2 = UISwipeGestureRecognizer()
wischen2.addTarget(self, action: #selector(handleMenuToggle))
wischen2.direction = .left
wischen2.numberOfTouchesRequired = 1
view.addGestureRecognizer(wischen2)
}
#objc func handleMenuToggle() {
delegate?.handleMenuToggle(forMenuOption: nil)
}
#objc func showProfile() {
let vc: AProfileViewController!
vc = AProfileViewController()
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true)
}
func configureNavigationBar() {
navigationController?.navigationBar.barTintColor = .darkGray
navigationController?.navigationBar.barStyle = .black
navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.font: UIFont(name: "Noteworthy", size: 22)!, NSAttributedString.Key.foregroundColor: UIColor.white]
//navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
navigationItem.title = "Mobile Job Board"
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_menu_white_3x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleMenuToggle))
navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_mail_outline_white_2x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(showCreateNewArticle))
}
//MARK: Table View
//skiped table view configuration
}
// - MARK: Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifer, for: indexPath) as! FeedCell
imageFiles[indexPath.row].getDataInBackground { (data, error) in
if let imageData = data {
if let imageToDisplay = UIImage(data: imageData) {
cell.postImage.image = imageToDisplay
}
}
}
cell.descriptionLabel.text = comments[indexPath.row]
cell.userButton.setTitle("\(usernames[indexPath.row]) \(lastnames[indexPath.row])", for: UIControl.State.normal)
cell.userButton.addTarget(self, action: #selector(showProfile), for: .touchUpInside)
return cell
}
//skiped
}
Thanks a lot!
Tom
The issue here is that your button works on a selector and it has no idea about the sender or where it was called from.
I would do this by creating a custom table view cell (e.g. FeedCell) which allows you to set a delegate (e.g. FeedCellDelegate). Set your class as the delegate for the cell and pass into the cell it's current indexPath. You can then return the indexPath in the delegate call.
Example: Note that code has been removed for simplicity and this code has not been tested. This is simply to guide you in the right direction.
View Controller
import UIKit
class HomeController: UIViewController {
// stripped additional information for example
func showProfile(_ username: String) {
let vc: AProfileViewController!
vc = AProfileViewController()
vc.username = username
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true)
}
}
extension HomeController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifer, for: indexPath) as! FeedCell
cell.delegate = self
cell.descriptionLabel.text = comments[indexPath.row]
cell.userButton.setTitle("\(usernames[indexPath.row]) \(lastnames[indexPath.row])", for: UIControl.State.normal)
cell.setIndex(indexPath)
return cell
}
}
extension HomeController: FeedCellDelegate {
func didPressButton(_ indexPath: IndexPath) {
let userName = usernames[indexPath.row]
showProfile(username)
}
}
Feed Cell
import UIKit
protocol FeedCellDelegate {
didPressButton(_ indexPath: IndexPath)
}
class FeedCell: UICollectionViewCell {
var delegate: FeedCellDelegate?
var indexPath: IndexPath
#IBOutlet weak var userButton: UIButton
setIndex(_ indexPath: IndexPath) {
self.indexPath = indexPath
}
#IBAction userButtonPressed() {
if(delegate != nil) {
delegate?.didPressButton(indexPath)
}
}
}
You can generically and in a type safe way get the parent responder of any responder with:
extension UIResponder {
func firstParent<T: UIResponder>(ofType type: T.Type ) -> T? {
return next as? T ?? next.flatMap { $0.firstParent(ofType: type) }
}
}
So:
Get the parent tableviewCell of your button in the target action function
Ask your tableview for the index path
Use the index path.row to index into your users array:
#objc func showProfile(_ sender: UIButton) {
guard let cell = firstParent(ofType: UITableViewCell.self),
let indexPath = tableView.indexPath(for: cell) else {
return
}
let user = users[indexPath.row]
... do other stuff here ...
}
I am making a simple tableview with a customCell. and a searchBar above. but getting a strange behavior from tableView. customCell is showing but above it defaultCell is showing as well and data is getting populated into the defaultCell though i am setting data on my customCell.
this is the output i am getting
https://i.imgur.com/xsTIsiz.png
if you look at it closely you will see my custom cell UI is showing under the default cell.
My custom cell:
https://i.imgur.com/J4UpRle.png
This is my code from viewcontroller
import UIKit
class SuraSearchController: UIViewController {
let searchController = UISearchController(searchResultsController: nil)
let reciters = [Reciter(name: "Abdul Basit Abdus Samad", downloadUrl: ""),
Reciter(name: "Abdul Rahman Al-Sudais", downloadUrl: ""),
Reciter(name: "Ali Bin Abdur Rahman Al Huthaify", downloadUrl: ""),
Reciter(name: "Mishary Rashid Alafasy", downloadUrl: ""),
Reciter(name: "Cheik Mohamed Jibril", downloadUrl: ""),
Reciter(name: "Mohamed Siddiq El-Minshawi", downloadUrl: ""),
Reciter(name: "Mahmoud Khalil Al-Hussary", downloadUrl: ""),
Reciter(name: "Ibrahim Walk (English Only)", downloadUrl: ""),
Reciter(name: "Abu Bakr Al Shatri", downloadUrl: "")]
var filteredReciters = [Reciter]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// definesPresentationContext = true
initNavBar()
initTableView()
// Do any additional setup after loading the view.
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
func initNavBar() {
// show navbar
self.navigationController?.setNavigationBarHidden(false, animated: true)
// set search bar delegates
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
// Customize Search Bar
searchController.searchBar.placeholder = "Search Friends"
let myString = "Cancel"
let myAttribute = [ NSAttributedStringKey.foregroundColor: UIColor.white ]
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = myString
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes(myAttribute, for: .normal)
if #available(iOS 11.0, *) {
let scb = searchController.searchBar
scb.tintColor = UIColor.white
scb.barTintColor = UIColor.white
if let textfield = scb.value(forKey: "searchField") as? UITextField {
textfield.textColor = UIColor.blue
if let backgroundview = textfield.subviews.first {
// Background color
backgroundview.backgroundColor = UIColor.white
// Rounded corner
backgroundview.layer.cornerRadius = 10
backgroundview.clipsToBounds = true
}
}
}
// Set search bar on navbar
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
}
func initTableView() {
tableView.dataSource = self
tableView.delegate = self
tableView.register(UINib(nibName: "SuraSearchCell", bundle: nil), forCellReuseIdentifier: "SuraSearchCell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredReciters = reciters.filter({( reciter : Reciter) -> Bool in
return reciter.name.lowercased().contains(searchText.lowercased())
})
tableView.reloadData()
}
func isFiltering() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
extension SuraSearchController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
return filteredReciters.count
}
return reciters.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "SuraSearchCell", for: indexPath) as? SuraSearchCell {
let candy: Reciter
if isFiltering() {
candy = filteredReciters[indexPath.row]
} else {
candy = reciters[indexPath.row]
}
cell.textLabel!.text = candy.name
return cell
}
return UITableViewCell()
}
}
extension SuraSearchController: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
And code of the tableview cell
import UIKit
class SuraSearchCell: UITableViewCell {
#IBOutlet weak var itemTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configureCell(item: Reciter) {
itemTitle.text = item.name
}
}
cell.textLabel is default UITableViewCell property. you just need to set value to your customCell itemTitle label.
Replace cell.textLabel!.text = candy.name with cell. itemTitle!.text = candy.name like below.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "SuraSearchCell", for: indexPath) as? SuraSearchCell {
let candy: Reciter
if isFiltering() {
candy = filteredReciters[indexPath.row]
} else {
candy = reciters[indexPath.row]
}
cell. itemTitle!.text = candy.name
//OR
cell.configureCell(candy) // IF your candy is of Reciter type
return cell
}
return UITableViewCell()
}
There may be a mistake with reuse identifier name. Match the name with the used one in the code.
Let update cellForRowAtIndex as below
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "SuraSearchCell", for: indexPath) as? SuraSearchCell {
let candy: Reciter
if isFiltering() {
candy = filteredReciters[indexPath.row]
} else {
candy = reciters[indexPath.row]
}
cell.configureCell(candy)
return cell
}
return UITableViewCell()
}
My ViewController contains a Table View. I am getting data from JSON and already able to show this values in my Table view cell. There is Search bar as well for filtering purpose.
I want to get my Table View Cell Text as my Search bar
placeholder when that particular cell is clicked.
I want to get Baramunda as my Placeholder text while that particular cell is clicked.
My code for the same is below.
func searchBar(){
let searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50));
searchBar.delegate = self
searchBar.showsScopeBar = true
searchBar.tintColor = UIColor.green
self.view .addSubview(searchBar)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText == ""
{
tableView.alpha = 0.0
searchBar.placeholder = "Search Location Here"
parseData()
}
else {
if searchBar.selectedScopeButtonIndex == 0 {
tableView.alpha = 1.0
areaNameArr = areaNameArr.filter({ (anyobject) -> Bool in
return (anyobject.lowercased.contains(searchText.lowercased()))
})
}
else {
print("Do Nothing")
}
}
self.tableView.reloadData()
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return areaNameArr.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell" )
cell?.backgroundColor = UIColor .black
cell?.textLabel?.textColor = UIColor .white
cell?.textLabel?.text = areaNameArr[indexPath.row] as? String
return cell!
}
You need to declare your searchBar variable global to access outside your function searchBar().
class ViewController: UIViewController{
let searchBar = UISearchBar()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//here goes your code of viewDidLoad
}
//This is your function for creating search bar ,only first line is changed and rest is same
func searchBar(){
searchBar.frame(forAlignmentRect: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50))
searchBar.delegate = self
searchBar.showsScopeBar = true
searchBar.tintColor = UIColor.green
self.view .addSubview(searchBar)
}
}
// this delegate method is called when cell is tapped
// remember to connect delegate from storyboard or you can use self.tableView.delegate = self
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//here get the text from tapped cell index
let tappedCellText = areaNameArr[indexPath.row] as? String
// here set placeholder
searchBar.placeholder = tappedCellText
// when cell is clicked you need to set text of search bar to nil or blank otherwise you would not be able to see placeholder text because some text is already there in search bar
searchBar.text = ""
// if you don't cleanup text from search bar you will be required to delete text from search bar manually to see placeholder text
}
Let me know if this doesn't help you
I don't have xcode right now but following should work.
Add following code in your didSelectCell delegate method of your tableView
var searchTextField: UITextField? = searchBar.value(forKey: "searchField") as? UITextField
if searchTextField!.responds(to: #selector(getter: UITextField.attributedPlaceholder)) {
let attributeDict = [NSAttributedStringKey.foregroundColor: UIColor.white]
searchTextField!.attributedPlaceholder = NSAttributedString(string: "Search", attributes: attributeDict)
}
The goal of the UITableView is to display names with a custom styled checkbox beside it. But somehow when I tap the checkbox, multiple checkboxes in the UITableView get selected.
I've googled and implemented multiple solutions from different questions but none of them seem to work.
Custom UITableViewCell
import UIKit
class NameTableViewCell: UITableViewCell {
var name = UILabel()
let checkboxImage_unchecked = UIImage(named: "cellViewCheckbox_unchecked")
let checkboxImage_checked = UIImage(named: "cellViewCheckbox_checked")
let checkbox:UIButton
weak var delegate:HandleCellInteractionDelegate?
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
self.checkbox = UIButton(frame: CGRectMake(35, 16, self.checkboxImage_unchecked!.size.width/2, self.checkboxImage_unchecked!.size.height/2))
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = UIColor.transparant()
self.checkbox.setBackgroundImage(self.checkboxImage_unchecked, forState: .Normal)
self.checkbox.setBackgroundImage(self.checkboxImage_checked, forState: .Selected)
self.name = UILabel(frame: CGRectMake(97,18,200, 30))
self.addSubview(self.name)
self.addSubview(self.checkbox)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func checkboxPushed(sender:AnyObject){
self.checkbox.selected = !self.checkbox.selected
self.delegate?.didPressButton(self)
}
In the protocol I declare a method didPressButton as a callback for when the button is pressed (the delegate needs to implement it)
Protocol
import UIKit
protocol HandleCellInteractionDelegate:class {
func didPressButton(cell:NameTableViewCell)
}
The ViewController
import UIKit
class NameViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, HandleCellInteractionDelegate{
var nameView:NameView {
get {
return self.view as! NameView
}
}
var names:[Name]?
var firstLetters:[String]?
let cellIdentifier = "nameCell"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//self.view.backgroundColor = UIColor.darkSalmon()
}
override func loadView() {
let bounds = UIScreen.mainScreen().bounds
self.view = NameView(frame: bounds)
if(NSUserDefaults.standardUserDefaults().objectForKey("gender") !== nil){
self.loadJSON()
self.getFirstLetters();
self.nameView.tableView.delegate = self;
self.nameView.tableView.dataSource = self;
self.nameView.tableView.registerClass(NameTableViewCell.classForCoder(), forCellReuseIdentifier: "nameCell")
self.nameView.tableView.separatorStyle = .None
self.nameView.tableView.rowHeight = 66.5 }
}
func loadJSON(){
let path = NSBundle.mainBundle().URLForResource("names", withExtension: "json")
let jsonData = NSData(contentsOfURL: path!)
self.names = NamesFactory.createFromJSONData(jsonData!, gender: NSUserDefaults.standardUserDefaults().integerForKey("gender"))
}
func getFirstLetters(){
var previous:String = ""
for name in self.names! {
let firstLetter = String(name.name.characters.prefix(1))
if firstLetter != previous {
previous = firstLetter
self.firstLetters?.append(firstLetter)
print(firstLetter)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.names!.count)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return nameCellAtIndexPath(indexPath)
}
func nameCellAtIndexPath(indexPath:NSIndexPath) -> NameTableViewCell {
let cell = self.nameView.tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! NameTableViewCell
self.setNameForCell(cell, indexPath: indexPath)
cell.delegate = self;
cell.checkbox.addTarget(cell, action: "checkboxPushed:", forControlEvents: .TouchUpInside)
return cell
}
func setNameForCell(cell:NameTableViewCell, indexPath:NSIndexPath) {
let item = self.names![indexPath.row] as Name
cell.name.text = item.name
}
func didPressButton(cell: NameTableViewCell) {
print(cell)
}
}
What am I doing wrong & how can I fix it?
You must have to save the check box state. Add one more attribute say checkboxStatus to your Model(Name).I hope you will have indexpath.row.
After this line in nameCellAtIndexPath
cell.delegate = self;
set the tag like this
cell.tag = indexPath.row;
In the delegate method,
func didPressButton(cell: NameTableViewCell) {
print(cell)
let item = self.names![cell.tag] as Name
item.checkboxstatus = cell.checkbox.selected
}
Just one crucial change,
func nameCellAtIndexPath(indexPath:NSIndexPath) -> NameTableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! NameTableViewCell
self.setNameForCell(cell, indexPath: indexPath)
cell.delegate = self;
cell.tag = indexPath.row;
cell.checkbox.addTarget(cell, action: "checkboxPushed:", forControlEvents: .TouchUpInside)
let item = self.names![cell.tag] as Name
cell.checkbox.selected = item.checkboxstatus
return cell
}
Cheers!
This happens because the UITableViewCell are being reused.
You changed the cell when you press the checkbox, you need to keep track of that in your data source model.
In cellForRowAtIndexPath you have to add a condition to check if that checkbox was checked or not. Then you display the appropriate view accordingly.
Edit:
In your example you need to check in nameCellAtIndexPath if the checkbox is checked or not and then display it correct.