Why an extension is blocking a touch event in tableViewCells? - ios

My UITableView used to work and sudendly it stopped to act as it was intended. I've found the segues are still doable but not just touching the cell, you need to hold the button and slide and then it does the segue. The delegates methods aren't called when a single touch is done. After being researching for a while, I think the best idea is to post my issue here, because looking in stackoverflow, seems that nobody have a similar issue.
I can click on the shop and the shop gets selected, but I have to maintain the button pressed, not a lot of time, a few miliseconds, almost instantly. The row turns gray but nothing happen. If I release the button then nothing happens too. if I slide then it does the segue. There is nowhere in my code or even in my whole project were I have added a single gesture. I've found an extension in my code that actually adds a gesture to my father viewController custom class. Find it in below
This is my table view code:
//
// showShopsTableView.swift
// yuApp
//
// Created by Alfredo on 16/5/16.
// Copyright © 2016 Alfredo. All rights reserved.
//
import UIKit
import CoreLocation
import MapKit
class showShopsTableView : ViewController, UITableViewDelegate, UITableViewDataSource,UISearchBarDelegate, UISearchResultsUpdating {
var selectedRow : shopsTableViewCell!
var index : Int = 0
#IBOutlet var searchBar: UISearchBar!
#IBOutlet weak var currentTableView: UITableView!
let resultSearchController = UISearchController(searchResultsController:nil)
var searchActive : Bool = false
var filtered:[String] = []
override func viewDidLoad(){
self.currentTableView?.delegate = self;
self.currentTableView?.dataSource = self;
super.viewDidLoad()
//Retrieve data asynchronously
let call = webApi()
call.retrieveOneCityShops{(success) in
self.doTableRefresh()
}
viewDidLayoutSubviews()
}
override func viewDidLayoutSubviews() {
/*
if let rect = self.navigationController?.navigationBar.frame {
let y = rect.size.height + rect.origin.y
self.currentTableView.contentInset = UIEdgeInsetsMake( y, 0, 0, 0)
}*/
}
//Segue Method
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationViewController = segue.destinationViewController as! detailViewShop
sendVariablesToDetailView(destinationViewController)
}
//Set variables in the next view controller before do a segue
func sendVariablesToDetailView(destinationViewController : detailViewShop){
destinationViewController.dataSend = self.selectedRow
destinationViewController.titleText = self.selectedRow.shopName.text
destinationViewController.descriptionText = self.selectedRow.shopDescription.text
destinationViewController.imageOutlet = self.selectedRow.shopImg
destinationViewController.shopCategory = self.selectedRow.label1.text
destinationViewController.brands = self.selectedRow.label2.text
}
//TableView Delegate Methods
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = tableView.cellForRowAtIndexPath(indexPath)! as! shopsTableViewCell
self.performSegueWithIdentifier("cell", sender: self)
}
func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
/*
if(searchActive) {
return filtered.count
}
*/
return TableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : shopsTableViewCell = tableView.dequeueReusableCellWithIdentifier("shopCell", forIndexPath: indexPath) as! shopsTableViewCell
cell.shopName.text = TableData[indexPath.row]
cell.shopDescription.text = values[indexPath.row].address
cell.label1.text = values[indexPath.row].city
cell.label2.text = values[indexPath.row].distance + "Km"
cell.shopImg = UIImageView.init(image: UIImage.init(named: "versace_shop.jpg"))
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 125
}
//SearchBar Delegate Methods
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchActive = true;
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
filtered = TableData.filter({ (text) -> Bool in
let tmp: NSString = text
let range = tmp.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return range.location != NSNotFound
})
if(filtered.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.currentTableView.reloadData()
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
currentTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func doTableRefresh(){
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.currentTableView.reloadData()
return
})
}
}
EDIT: to remove all images that were showing the property userInteractionEnabled to TRUE. I've finally found the source of the issue. It has nothing to do with storyBoard. Is an extension I wrote for my main custom class, viewController (and so parent of this window too). The extension is that one:
import Foundation
import UIKit
// Put this piece of code anywhere you like
extension ViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
It was called at the very beginning of my custom viewController class, right after super.viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
// Hide keyboard when tap away
self.hideKeyboardWhenTappedAround()
I've commented super.viewDidLoad() in my showShopsTableView class and now is working just fine.
I'm not really sure to understand why this piece of code is blocking the touch event of my tableView, so if somebody wants to explain it, I will accept his answer. Also some points to my question would be very appreciated if you found it usefull.
In any case, thanks a lot to all that spend a minute here.

Related

Search bar not working in Swift 4 IOS App

I have implemented a search bar to search for users, but nothing shows up in the table view when I search for something. I have attached a picture of my view controller below.
View Controller:
This view controller shows a list of all the users and the search bar is supposed to help the user find a username.
import UIKit
class FindFriendsViewController: UIViewController {
var users = [User]()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var searchItem = [String]()
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
tableView.rowHeight = 71
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:)))
tap.cancelsTouchesInView = false
self.view.addGestureRecognizer(tap)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UserService.usersExcludingCurrentUser { [unowned self] (users) in
self.users = users
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
extension FindFriendsViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return searchItem.count
} else {
return users.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FindFriendsCell") as! FindFriendsCell
// let user = users[indexPath.row]
var usernamesArr = [String]()
for user in users {
usernamesArr.append(user.username)
}
if searching {
cell.textLabel?.text = searchItem[indexPath.row]
} else {
cell.textLabel?.text = usernamesArr[indexPath.row]
cell.delegate = self
configure(cell: cell, atIndexPath: indexPath)
}
return cell
}
func configure(cell: FindFriendsCell, atIndexPath indexPath: IndexPath) {
let user = users[indexPath.row]
cell.usernameLabel.text = user.username
cell.followButton.isSelected = user.isFollowed
}
}
extension FindFriendsViewController: FindFriendsCellDelegate {
func didTapFollowButton(_ followButton: UIButton, on cell: FindFriendsCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
followButton.isUserInteractionEnabled = false
let followee = users[indexPath.row]
FollowService.setIsFollowing(!followee.isFollowed, fromCurrentUserTo: followee) { (success) in
defer {
followButton.isUserInteractionEnabled = true
}
guard success else { return }
followee.isFollowed = !followee.isFollowed
self.tableView.reloadRows(at: [indexPath], with: .none)
}
}
}
extension FindFriendsViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
var usernamesArr = [String]()
for user in users {
usernamesArr.append(user.username)
}
searchItem = usernamesArr.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
searching = true
tableView.reloadData()
}
}
I am thinking about different problems that may occur in your code. You need to set the search bar delegate and the search Result Updater:
yourSearchController.searchBar.delegate = self
yourSearchController.searchResultsUpdater = self
If you don't have a controller, but directly the search bar:
yourSearchBar.delegate = self
yourSearchBar.searchResultsUpdater = self
And this as your delegate:
extension MasterViewController: UISearchBarDelegate {
// MARK: - UISearchBar Delegate
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
extension MasterViewController: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
Or maybe you are missing something like updating. Check the data's path excluding the problem one for time. First, you enter the text inside the search bar, after that check in the code where the text goes and what happens. Did you update the table view when search bar is in end editing status?
If it doesn't help you, check this wonderful tutorial which I followed times ago: Search Bar iOS
try for search username using this method to filter
searchItem = users.filter({ (objUser) -> Bool in
return (objUser.username?.lowercased() ?? "").starts(with: searchBar.text!.trim().lowercased())
})
Hope this will help you.

selecting cell in tableview while UISearchController is active, doesnt present next view?

I have made a tableview where you can select a cell, and then the viewcontroller will perform a segue to the next view, which works perfectly fine when you are not using the searchcontroller.
Then when you are using the searchcontroller, it filters the tableview as it should, and the segue is called in didSelectRowAtIndexPath, and the prepareForSegue is called. The problem then is that the view it should segue to is not presented? I can see that the code in the class connected to the view is running, so the segue is performed, it is just the view that does not follow. What am i missing
class CompanyListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
#IBOutlet weak var tableView: UITableView!
let objectMapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var activityIndicatorView: SWActivityIndicatorView!
var resultSearchController: UISearchController!
var allCompanies: [Company] = []
var filteredCompanies = [Company]()
override func viewDidLoad() {
super.viewDidLoad()
// set delegates
tableView.delegate = self
tableView.dataSource = self
configureSearchController()
// initialize activity indicator view
self.activityIndicatorView = SWActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
activityIndicatorView.hidesWhenStopped = true
activityIndicatorView.color = UIColor.lightGrayColor()
self.view.addSubview(activityIndicatorView)
self.activityIndicatorView.center = self.view.center
activityIndicatorView.startAnimating()
// fetch all records from backend
fetchAllRecords({(errors: [NSError]?) -> Void in if errors != nil {print(errors)}})
}
func configureSearchController() {
// Initialize and perform a minimum configuration to the search controller.
// Search Bar
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController?.searchBar.autocapitalizationType = .None
self.tableView.tableHeaderView = self.resultSearchController?.searchBar
resultSearchController?.dimsBackgroundDuringPresentation = false
self.resultSearchController?.searchResultsUpdater = self
definesPresentationContext = true
}
// search delegate method
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filterContentForSearchText(searchController.searchBar.text!)
}
// Filter method, which filters by companyName, and reloads tableview
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredCompanies = allCompanies.filter { company in
return company._companyName!.lowercaseString.containsString(searchText.lowercaseString)
}
tableView.reloadData()
}
// fetch all records from backend
func fetchAllRecords(completionHandler: (errors: [NSError]?) -> Void) {
let scanExpression = AWSDynamoDBScanExpression()
objectMapper.scan(Company.self, expression: scanExpression) { (response: AWSDynamoDBPaginatedOutput?, error: NSError?) in
dispatch_async(dispatch_get_main_queue(), {
// if error
if let error = error {
completionHandler(errors: [error]);
}
//if success
else {
self.allCompanies = response!.items as! [Company]
self.tableView.reloadData()
self.activityIndicatorView.stopAnimating()
}
})
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if resultSearchController.active && resultSearchController.searchBar.text != "" {
return filteredCompanies.count
}
return allCompanies.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:CompanyListTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("companyCell") as! CompanyListTableViewCell
// set the text from the data model
let company:Company?
if resultSearchController.active && resultSearchController.searchBar.text != "" {
company = self.filteredCompanies[indexPath.row]
} else {
company = self.allCompanies[indexPath.row]
}
cell.titleLabel.text = company!._companyName
cell.imageview?.image = UIImage(named: "placeholder")
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("segueToProfile", sender: self)
}
// send selected company with segue to profile
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segueToProfile"){
let indexPath = tableView.indexPathForSelectedRow
//tableView.deselectRowAtIndexPath(indexPath!, animated: true)
let selectedRow = indexPath!.row
let profileVC = segue.destinationViewController as! ProfileViewController
if resultSearchController.active{
print(filteredCompanies[selectedRow])
profileVC.company = filteredCompanies[selectedRow]
} else {
profileVC.company = allCompanies[selectedRow]
}
}
}
}
The console is saying this, but i dont know if that has anything to do with this?
2016-11-26 15:54:07.300 Lostandfound[949:2474251] Warning: Attempt to present on which is already presenting
Here is the example of TableView with SearchBar control.You should remove didSelectRowAtIndexPath method and use prepareForSegue method for Determine the selected row in TableView.Like this...
Example:
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate
{
#IBOutlet weak var SerchBar: UISearchBar!
#IBOutlet weak var TableView: UITableView!
var searchActive : Bool = false
var data = ["San Francisco","New York","San Jose","Chicago","Los Angeles","Austin","Seattle"]
var filtered:[String] = []
override func viewDidLoad()
{
super.viewDidLoad()
}
private func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if(searchActive)
{
return filtered.count
}
return data.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = self.TableView.dequeueReusableCell(withIdentifier: "Cell") as! TableViewCell
if(searchActive)
{
cell.Label.text = filtered[indexPath.row]
}
else
{
cell.Label.text = data[indexPath.row]
}
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any!)
{
if let cell = sender as? TableViewCell
{
let i = TableView.indexPath(for: cell)!.row
if segue.identifier == "segue1"
{
if(searchActive)
{
let name1 = segue.destination as! SecondView
name1.str = self.filtered[i]
}
else
{
let name1 = segue.destination as! SecondView
name1.str = self.data[i]
}
}
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
{
filtered = data.filter({ (text) -> Bool in
let tmp: NSString = text as NSString
let range = tmp.range(of: searchText, options: .caseInsensitive)
return range.location != NSNotFound
})
if(filtered.count == 0)
{
searchActive = false;
}
else
{
searchActive = true;
}
self.TableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar)
{
searchActive = true;
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar)
{
searchActive = false;
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar)
{
searchActive = false;
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
{
searchActive = false;
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
}
Your SecondView class is:
import UIKit
class SecondView: UIViewController
{
#IBOutlet weak var label: UILabel!
var str:String!
override func viewDidLoad()
{
super.viewDidLoad()
self.label.text = str
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
}
And your TableViewCell is:
import UIKit
class TableViewCell: UITableViewCell
{
#IBOutlet weak var Label: UILabel!
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
}

Why my search bar is not working?

I have tried to solve this for a couple of hours. I am a newbie in IOS and want to develop a bucket list with a search bar. The codes have no problem in compilation but when i make input in the search bar, it won't show any search result. I don't know where the problem is.
btw: I have two suspects:
1. I tried to print the status, and it seems the searchBar is never active. How to open it?
2. Does it have anything to do with var missions' data
type as Mission? It is something I am not familiar with.
Please see the following codes:
import UIKit
class BucketListViewController:UITableViewController, MissionDetailsViewControllerDelegate, MissionEditViewControllerDelegate, UISearchBarDelegate{
var searchController = UISearchController(searchResultsController: nil)
#IBOutlet weak var searchBar: UISearchBar!
var searchActive : Bool = false
var filtered:[String] = []
var missions:[String] = ["Sky diving", "Live in Hawaii"]
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
self.searchDisplayController!.searchResultsTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchActive = true;
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
filtered = missions.filter({ (text) -> Bool in
let tmp: NSString = text
let range = tmp.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return range.location != NSNotFound
})
if(filtered.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// dequeue the cell from our storyboard
let cell = tableView.dequeueReusableCellWithIdentifier("MyCell")!
if(searchActive){
print("search active!")
cell.textLabel?.text = filtered[indexPath.row]
} else {
cell.textLabel?.text = missions[indexPath.row]
}
return cell
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive){
return filtered.count
} else {
return missions.count
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "AddNewMission" {
let navigationController = segue.destinationViewController as! UINavigationController
let controller = navigationController.topViewController as! MissionDetailsViewController
controller.delegate = self
}
if segue.identifier == "EditMission" {
let navigationController = segue.destinationViewController as! UINavigationController
let controller = navigationController.topViewController as! MissionEditViewController
controller.delegate = self
}
}
func missionDetailsViewController(controller: MissionDetailsViewController, didFinishAddingMission mission: String) {
dismissViewControllerAnimated(true, completion: nil)
missions.append(mission)
tableView.reloadData()}
func missionEditViewController(controller: MissionEditViewController, didFinishEditingMission mission: String, atIndexPath indexPath: Int) {
dismissViewControllerAnimated(true, completion: nil)
missions[indexPath] = mission
tableView.reloadData()
}
}
From the project you have shared there is a problem in your cellForRowAtIndexPath method.
replace
cell.textLabel?.text = filtered[indexPath.row] as? String
with
cell.textLabel?.text = filtered[indexPath.row].details
And it will work fine.
Your complete code will be:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// dequeue the cell from our storyboard
let cell = tableView.dequeueReusableCellWithIdentifier("MissionCell")!
// if the cell has a text label, set it to the model that is corresponding to the row in array
if(searchActive){
print("search active!")
cell.textLabel?.text = filtered[indexPath.row].details
} else {
cell.textLabel?.text = missions[indexPath.row].details
}
return cell
}

Swift Redundant conformance

I am learning iOS. And now local database on iOS. I'm from Android and finding local DB a bit difficult to understand. I wrote a question which brought me several down votes (instead of simple use of CoreStore or Sync or MagicalRecord). I then started reading this tutorial. After downloading the sample project I'm getting Redundant conformance of 'BeerListViewController' to protocol 'UITableViewDataSource' error in BeerListViewController. Please tell me how can I get it solved.
Here is the code:
import UIKit
import Foundation
class BeerListViewController: UITableViewController {
#IBOutlet weak var sortByControl: UISegmentedControl!
#IBOutlet weak var searchBar: UISearchBar!
let sortKeyName = "name"
let sortKeyRating = "beerDetails.rating"
let wbSortKey = "wbSortKey"
//------------------------------------------
// Rating
var amRatingCtl: AnyObject!
let beerEmptyImage: UIImage = UIImage(named: "beermug-empty")!
let beerFullImage: UIImage = UIImage(named: "beermug-full")!
//#####################################################################
// MARK: - Initialization
required init(coder aDecoder: NSCoder) {
// Automatically invoked by UIKit as it loads the view controller from the storyboard.
amRatingCtl = AMRatingControl(location: CGPointMake(190, 10),
emptyImage: beerEmptyImage,
solidImage: beerFullImage,
andMaxRating: 5)
// A call to super is required after all variables and constants have been assigned values but before anything else is done.
super.init(coder: aDecoder)!
}
//#####################################################################
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Allows data to be passed to the new view controller before the new view is displayed.
// "destinationViewController" must be cast from its generic type (AnyObject) to the specific type used in this app
// (BeerDetailViewController) before any of its properties can be accessed.
let controller = segue.destinationViewController as? BeerDetailViewController
if segue.identifier == "editBeer" {
controller!.navigationItem.rightBarButtonItems = []
//------------------------------------------------------------------------------------
} else if segue.identifier == "addBeer" {
controller!.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel",
style: UIBarButtonItemStyle.Plain,
target: controller,
action: "cancelAdd")
controller!.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done",
style: UIBarButtonItemStyle.Done,
target: controller,
action: "addNewBeer")
}
}
//#####################################################################
// MARK: - UIViewController - Responding to View Events
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//------------------------------------------
// Sorting Key
if !(NSUserDefaults.standardUserDefaults().objectForKey(wbSortKey) != nil) {
// User's sort preference has not been saved. Set default to sort by rating.
NSUserDefaults.standardUserDefaults().setObject(sortKeyRating, forKey: wbSortKey)
}
// Keep the sort control in the UI in sync with the means by which the list is sorted.
if NSUserDefaults.standardUserDefaults().objectForKey(wbSortKey) as! String == sortKeyName {
sortByControl.selectedSegmentIndex = 1
}
//------------------------------------------
fetchAllBeers()
// Cause tableView(cellForRowAtIndexPath) to be called again for every visible row in order to update the table.
tableView.reloadData()
}
//#####################################################################
// MARK: - UIViewController - Managing the View
// viewDidLoad() is called after prepareForSegue().
override func viewDidLoad() {
super.viewDidLoad()
//------------------------------------------
tableView.contentOffset = CGPointMake(0, 44)
}
//#####################################################################
// MARK: - Action Methods
#IBAction func sortByControlChanged(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
NSUserDefaults.standardUserDefaults().setObject(sortKeyRating, forKey: wbSortKey)
fetchAllBeers()
tableView.reloadData()
case 1:
NSUserDefaults.standardUserDefaults().setObject(sortKeyName, forKey: wbSortKey)
fetchAllBeers()
tableView.reloadData()
default:
break
}
}
//#####################################################################
// MARK: - MagicalRecord Methods
func fetchAllBeers() {
}
//#####################################################################
func saveContext() {
}
//#####################################################################
}
//#####################################################################
// MARK: - Table View Data Source
extension BeerListViewController: UITableViewDataSource { // THE ERROR APPEARS HERE
//#####################################################################
// MARK: Configuring a Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
//#####################################################################
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
//#####################################################################
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier)
configureCell(cell!, atIndex: indexPath)
return cell!
}
//#####################################################################
// MARK: Helper Methods
func configureCell(cell: UITableViewCell, atIndex indexPath: NSIndexPath) {
//------------------------------------------
// Rating
let ratingText = ""
let myRect = CGRect(x:250, y:0, width:200, height:50)
var ratingLabel = UILabel(frame: myRect)
if !(cell.viewWithTag(20) != nil) {
ratingLabel.tag = 20
ratingLabel.text = ratingText
cell.addSubview(ratingLabel)
} else {
ratingLabel = cell.viewWithTag(20) as! UILabel
}
//----------------------
ratingLabel.text = ratingText
}
//#####################################################################
// MARK: Inserting or Deleting Table Rows
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
//#####################################################################
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
// When the commitEditingStyle method is present in a view controller, the table view will automatically enable swipe-to-delete.
if (editingStyle == .Delete) {
}
}
//#####################################################################
}
//#####################################################################
// MARK: - Search Bar Delegate
extension BeerListViewController: UISearchBarDelegate {
// MARK: Editing Text
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text != "" {
performSearch()
} else {
fetchAllBeers()
tableView.reloadData()
}
}
//#####################################################################
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchBar.showsCancelButton = true
}
//#####################################################################
// MARK: Clicking Buttons
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchBar.resignFirstResponder()
searchBar.text = ""
searchBar.showsCancelButton = false
fetchAllBeers()
tableView.reloadData()
}
//#####################################################################
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
// This method is invoked when the user taps the Search button on the keyboard.
searchBar.resignFirstResponder()
performSearch()
}
//#####################################################################
// MARK: Helper Methods
func performSearch() {
tableView.reloadData()
}
//#####################################################################
// MARK: - Bar Positioning Delegate
// UISearchBarDelegate Protocol extends UIBarPositioningDelegate protocol.
// Method positionForBar is part of the UIBarPositioningDelegate protocol.
// This delegate method is required to prevent a gap between the top of the screen and the search bar.
// That happens because, as of iOS 7, the status bar is no longer a separate area but is directly drawn on top of the view controller.
func positionForBar(bar: UIBarPositioning) -> UIBarPosition {
// Tell the search bar to extend under the status bar area.
return .TopAttached
}
//#####################################################################
}
UITableViewController conforms to UITableViewDataSource by definition.
Just delete the protocol conformance
extension BeerListViewController { ...

Swift - Search Bar Initial Load

I am attempting to have a search functionality within my app. The search would basically search for user input in the Parse class in the backend.
So far the code below works very well for my case except that as soon as the view loads (prior to starting typing anything) it loads all the usernames in the backend in the table rows. As the user types letters, it filters. I want to have all the same functionality, except for showing all users in table rows as soon as view loads. How can this be achieved?
class FriendByUsernameTableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate {
var friendObject = FriendClass()
#IBOutlet weak var searchBar: UISearchBar!
var searchActive : Bool = false
var data:[PFObject]!
var filtered:[PFObject]!
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.delegate = self
search()
}
func search(searchText: String? = nil){
let query = PFQuery(className: "_User")
if(searchText != nil){
query.whereKey("appUsername", containsString: searchText)
}
query.findObjectsInBackgroundWithBlock { (results, error) -> Void in
self.data = results as? [PFObject]!
self.tableView.reloadData()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(self.data != nil){
return self.data.count
}
return 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
let obj = self.data[indexPath.row]
cell.textLabel!.text = obj["appUsername"] as? String
return cell
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchActive = true;
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
search(searchText)
}
}
The code is based on this tutorial: http://shrikar.com/parse-search-in-ios-8-with-swift/
try adding
if searchText!.stringByTrimmingCharactersInSet(.whitespaceCharacterSet()) != "" {
query.findObjectsInBackgroundWithBlock()
}
in your search function along with the block of course.
This way you won't query if you click on the search bar or if you tap a bunch of spaces.
Also, if you don't want users to be displayed upon load, don't call search() in your viewDidLoad()

Resources