What makes the searchbar option selectedScopeButtonIndexDidChange execute? - ios

I'm trying to learn the new UISearchController in swift. Not too many examples out there yet.
I have been looking at adding a scope option such that when a user selects the scope in the search the list is filtered by the scope. I've been looking at the programming iOS 8 O'Reilly book, examples. They have an example where the filtering is done in one screen.
I realize that the actual search will not work with the current scope values I have. All I'm after at the moment is to get that function working. I'm using this code to test ideas and then porting those ideas to my application.
If I enter a text search it all works, but when I try the scope, it doesn't. Here is the code. Any hints as to why the following code is never called:
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
println("In selectedScopeButtonIndexDidChange")
updateSearchResultsForSearchController(searcher)
}
Here is the complete code.
import UIKit
class ViewController: UITableViewController, UISearchBarDelegate, UISearchControllerDelegate, UISearchResultsUpdating {
var sectionNames = [String]()
var sectionData = [[String]]()
var originalSectionNames = [String]()
var originalSectionData = [[String]]()
var searcher = UISearchController()
var searching = false
override func prefersStatusBarHidden() -> Bool {
return true
}
override func viewDidLoad() {
//super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let s = NSString(contentsOfFile: NSBundle.mainBundle().pathForResource("states", ofType: "txt")!, encoding: NSUTF8StringEncoding, error: nil)!
let states = s.componentsSeparatedByString("\n") as [String]
var previous = ""
for aState in states {
// get the first letter
let c = (aState as NSString).substringWithRange(NSMakeRange(0,1))
// only add a letter to sectionNames when it's a different letter
if c != previous {
previous = c
self.sectionNames.append( c.uppercaseString )
// and in that case also add new subarray to our array of subarrays
self.sectionData.append( [String]() )
}
sectionData[sectionData.count-1].append( aState )
}
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.tableView.registerClass(UITableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "Header")
self.tableView.sectionIndexColor = UIColor.whiteColor()
self.tableView.sectionIndexBackgroundColor = UIColor.redColor()
// self.tableView.sectionIndexTrackingBackgroundColor = UIColor.blueColor()
// self.tableView.backgroundColor = UIColor.yellowColor()
self.tableView.backgroundView = { // this will fix it
let v = UIView()
v.backgroundColor = UIColor.yellowColor()
return v
}()
// in this version, we take the total opposite approach:
// we don't present any extra view at all!
// we already have a table, so why not just filter the very same table?
// to do so, pass nil as the search results controller,
// and tell the search controller not to insert a dimming view
// keep copies of the original data
self.originalSectionData = self.sectionData
self.originalSectionNames = self.sectionNames
let searcher = UISearchController(searchResultsController:nil)
self.searcher = searcher
searcher.dimsBackgroundDuringPresentation = false
searcher.searchResultsUpdater = self
searcher.delegate = self
// put the search controller's search bar into the interface
let b = searcher.searchBar
b.sizeToFit() // crucial, trust me on this one
b.autocapitalizationType = .None
b.scopeButtonTitles = ["All", "S", "X"] // won't show in the table
**b.delegate = self**
self.tableView.tableHeaderView = b
self.tableView.reloadData()
//self.tableView.scrollToRowAtIndexPath(
// NSIndexPath(forRow: 0, inSection: 0),
// atScrollPosition:.Top, animated:false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.sectionNames.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.sectionData[section].count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let s = self.sectionData[indexPath.section][indexPath.row]
cell.textLabel!.text = s
// this part is not in the book, it's just for fun
var stateName = s
stateName = stateName.lowercaseString
stateName = stateName.stringByReplacingOccurrencesOfString(" ", withString:"")
stateName = "flag_\(stateName).gif"
let im = UIImage(named: stateName)
cell.imageView!.image = im
return cell
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let h = tableView.dequeueReusableHeaderFooterViewWithIdentifier("Header") as UITableViewHeaderFooterView
if h.tintColor != UIColor.redColor() {
h.tintColor = UIColor.redColor() // invisible marker, tee-hee
h.backgroundView = UIView()
h.backgroundView!.backgroundColor = UIColor.blackColor()
let lab = UILabel()
lab.tag = 1
lab.font = UIFont(name:"Georgia-Bold", size:22)
lab.textColor = UIColor.greenColor()
lab.backgroundColor = UIColor.clearColor()
h.contentView.addSubview(lab)
let v = UIImageView()
v.tag = 2
v.backgroundColor = UIColor.blackColor()
v.image = UIImage(named:"us_flag_small.gif")
h.contentView.addSubview(v)
lab.setTranslatesAutoresizingMaskIntoConstraints(false)
v.setTranslatesAutoresizingMaskIntoConstraints(false)
h.contentView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("H:|-5-[lab(25)]-10-[v(40)]",
options:nil, metrics:nil, views:["v":v, "lab":lab]))
h.contentView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("V:|[v]|",
options:nil, metrics:nil, views:["v":v]))
h.contentView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("V:|[lab]|",
options:nil, metrics:nil, views:["lab":lab]))
}
let lab = h.contentView.viewWithTag(1) as UILabel
lab.text = self.sectionNames[section]
return h
}
// much nicer without section index during search
override func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]! {
return self.searching ? nil : self.sectionNames
}
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
println("In selectedScopeButtonIndexDidChange")
updateSearchResultsForSearchController(searcher)
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
let sb = searchController.searchBar
let target = sb.text
println("target is: \(target)")
if target == "" {
self.sectionNames = self.originalSectionNames
self.sectionData = self.originalSectionData
self.tableView.reloadData()
return
}
// we have a target string
self.sectionData = self.originalSectionData.map {
$0.filter {
let options = NSStringCompareOptions.CaseInsensitiveSearch
let found = $0.rangeOfString(target, options: options)
return (found != nil)
}
}.filter {$0.count > 0} // is Swift cool or what?
self.sectionNames = self.sectionData.map {prefix($0[0],1)}
self.tableView.reloadData()
}
}

The answer to this problem was to add:
b.delegate = self
into the viewDidLoad() section.
I've updated the code above with the new line added.

You will need to set the searchBar delegate explicitly
// put the search controller's search bar into the interface
let b = searcher.searchBar
b.delegate = self

Related

filtering and displaying searchbar results from firebase database

I am just starting to learn swift and firebase. I want to add a search bar that will allow users to search through my firebase database. This is what I want to get
I have added the searchbar, what I'm having problem with is the display of search result.
I created a container view that include Name, subdescription and logo like the image above and then set them up with this function
func searchResultContainer(){
searchResultView.addSubview(businesslogoView)
searchResultView.addSubview(businessNameLabel)
searchResultView.addSubview(businessSectorLabel)
//need x. y, width, height constraints for searchResult
searchResultView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
searchResultView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
searchResultView.heightAnchor.constraint(equalToConstant: 220).isActive = true
}
I then append the searchResult view to var bussinesarray. and then insert it into the tableview. Please see my code below
var businessArray = [NSDictionary]()
var filterBusiness = [NSDictionary]()
var ref : FIRDatabaseReference!
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.insertRows(at: [IndexPath(row: self.businessArray.count-1, section: 0)], with: UITableViewRowAnimation.automatic)
ref.child("Businesses").queryOrdered(byChild: "Basic-Info/business").observe(.childAdded, with: { (snapshot) in
view.addSubview(searchResultView)
searchResultContainer()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// if searchbar is not empty "", then return filtered businesses if the user is not typing anything return all businesses.
if searchController.isActive && searchController.searchBar.text !=
""{
return filterBusiness.count
}
return self.businessArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let business : NSDictionary?
if searchController.isActive && searchController.searchBar.text !=
""{
business = filterBusiness[indexPath.row]
}
else
{
business = self.businessArray[indexPath.row]
}
cell.textLabel?.text = business?["Business"] as? String
cell.detailTextLabel?.text = business?["handle"] as? String
return cell
}
func filterContent (searchText:String) {
self.filterBusiness = self.businessArray.filter{ Businesses in
let businessName = Businesses["Business"] as? String
return(businessName?.contains(searchText.lowercased()))!
}
tableView.reloadData()
}
func updateSearchResults(for searchController: UISearchController) {
// update the search results
filterContent(searchText: self.searchController.searchBar.text!)
}
I am not getting the search result from firebase DB, how do I correctly implement the search result from firebase DB? I am building everything programmatically, please a sample code with be greatly appreciated.
This tutorial was a great help for me in figuring out a similar implementation, see the code near the bottom of the tutorial.
http://shrikar.com/swift-ios-tutorial-uisearchbar-and-uisearchbardelegate/
Code adjustments beyond this tutorial included the below code. I still have some clean up that could be done around the if/else section however the two critical concepts for me was using the model and getting the target correct with: let temp: NSString = text.EntityName! as NSString
Model file:
class Dealer: NSObject{
var DealerNumber: String?
var EntityName: String?
//matchup all other firebase data fields
}
ViewController Adjustments
var dealerList = [Dealer]()
var filterDealers = [Dealer]()
---
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterDealers = dealerList.filter({ (text) -> Bool in
let temp: NSString = text.EntityName! as NSString
let range = temp.range(of: searchText, options: NSString.CompareOptions.caseInsensitive)
return range.location != NSNotFound
})
if(filterDealers.count == 0){
searchActive = false;
} else {
searchActive = true;
}
refreshTable()
}
----
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
var cell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)
if(searchActive){
cell?.textLabel?.text = filterDealers[indexPath.row].EntityName
cell?.detailTextLabel?.text = filterDealers[indexPath.row].DealerNumber
} else {
cell?.textLabel?.text = dealerList[indexPath.row].EntityName
cell?.detailTextLabel?.text = dealerList[indexPath.row].DealerNumber
}
return cell!;
}

Search results are cut off when searching TableView

My search is working correctly except the display is not working as expected. When I am performing a search, the last search result is slightly cut off and the portion of the display is all gray. I would expect it to remain the same background color as the rows.. Any help would be appreciated.
EDIT:
import UIKit
import Foundation
// For search
extension MasterViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
class MasterViewController: UITableViewController {
let searchController = UISearchController(searchResultsController: nil)
var filteredFighters = [Fighter]()
var detailViewController: DetailViewController? = nil
var objects = [Any]()
// Creates array of fighters
var fighterArray = [Fighter]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
// Access all the data from JSON
let JR = JSONReceiver()
fighterArray = JR.populateFighterJSONArray()
// Search related
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
navigationItem.title = "UFC Champions"
self.navigationController?.navigationBar.barTintColor = UIColor.black
self.navigationController?.navigationBar.tintColor = UIColor.white
self.navigationController?.navigationBar.titleTextAttributes = [ NSForegroundColorAttributeName : UIColor.white, NSFontAttributeName: UIFont(name: "Arial", size: 26)!]
}
// FOr search
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredFighters = fighterArray.filter { fighter in
return fighter.name.lowercased().contains(searchText.lowercased())
}
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.isCollapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/* DONT BELIEVE THIS IS NEEDED - DO NOT SEE IN PROFESSORS CODE
func insertNewObject(_ sender: Any) {
objects.insert(NSDate(), at: 0)
let indexPath = IndexPath(row: 0, section: 0)
self.tableView.insertRows(at: [indexPath], with: .automatic)
} */
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Determines which row was selected (e.g. which fighters, say element 1 points to fighter1)
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
// let stud = StudentsArray[indexPath.row]
let fighter: Fighter
if searchController.isActive && searchController.searchBar.text != "" {
fighter = filteredFighters[indexPath.row]
} else {
fighter = fighterArray[indexPath.row]
}
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
// Makes sure selected student points to the correct controller
controller.detailItem = fighter
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
// MARK: - Table View
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
return filteredFighters.count
}
return fighterArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let fighter: Fighter
if searchController.isActive && searchController.searchBar.text != "" {
fighter = filteredFighters[indexPath.row]
} else {
fighter = fighterArray[indexPath.row]
}
cell.textLabel!.text = fighter.name
cell.detailTextLabel?.text = fighter.record
cell.imageView?.image = UIImage(named: fighter.fighterImage)
self.tableView .sizeToFit()
// cell.detailTextLabel?.text = "fighterTest"
return cell
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return false
}
}

how to pass data from tableview to tableview like instagram? swift

If users search, the results come out on first table View(searchHome).
And If I select one cell, I can see detail info of this on to next tableView(bookDetail).
So in bookDetail, so only one cell exists like instagram.(it's like instagram my page. In my page I can see many pictures, but I select one, I can only 1 picture with detail info).
but the data of searchHome is not passed to the detailBook.
there are 3 classes with this issue.
One is class for passing data(BookAPIResult)
Another is class of UITableViewController for search(SearchHome)
The other is class of UITableViewController for detailIndo(bookDetail)
class BookAPIresult {
// thumbnail url
var thumbnail : String?
// book title
var title : String?
// book author
var author : String?
// book pub.
var pubnm : String?
// book description
var description : String?
// sellerID
var seller : String?
// list Price
var listPrice : String?
// selling Price
var sellPrice : String?
// UIImage for Thumbnail
var thumbnailImage : UIImage?
}
and SearchHome class is below.
class SearchHome: UITableViewController, UISearchBarDelegate, UISearchControllerDelegate{
// MARK: - Properties
let searchController = UISearchController(searchResultsController: nil)
// var barButton = UIBarButtonItem(title: "Search", style: .Plain, target: nil, action: nil)
let apiKey : String = "cbccaa3f2e893c245785c3b94d980b0c"
var searchString : String = ""
var list = Array<BookAPIresult>()
// MARK: - View Setup
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.searchController.delegate = self
//self.searchController.searchBar.text! = ""
//Setup the status bar
tableView.contentInset.top = 0
// Setup the Search Controller
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.searchBarStyle = UISearchBarStyle.Prominent
searchController.searchBar.sizeToFit()
self.definesPresentationContext = true
self.tableView.tableHeaderView = searchController.searchBar
//searchController.navigationItem.rightBarButtonItem = barButton
searchController.hidesNavigationBarDuringPresentation = true
// Setup the Scope Bar
searchController.searchBar.scopeButtonTitles = ["Title", "HashTag"]
//tableView.tableHeaderView = searchController.searchBar
// Setup Animation for NavigationBar
navigationController?.hidesBarsOnSwipe = true
searchController.hidesNavigationBarDuringPresentation = false
navigationController?.hidesBarsWhenKeyboardAppears = false
navigationController?.hidesBarsOnTap = true
navigationController?.hidesBarsWhenVerticallyCompact = true
self.refreshControl?.addTarget(self, action: #selector(SearchHome.handleRefresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
// declare hide keyboard swipe
let hideSwipe = UISwipeGestureRecognizer(target: self, action: #selector(SearchHome.hideKeyboardSwipe(_:)))
self.view.addGestureRecognizer(hideSwipe)
// searchController.searchBar.text = searchString
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar){
self.searchString = self.searchController.searchBar.text!
self.list.removeAll()
self.callBookAPI()
self.tableView.reloadData()
self.searchController.active = false
}
override func viewDidAppear(animated: Bool) {
self.searchController.active = false
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.list.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let row = self.list[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("ListCell") as! BookAPIResultCell
cell.title?.text = row.title
cell.author?.text = row.author
dispatch_async(dispatch_get_main_queue(),{ cell.thumb.image = self.getThumbnailImage(indexPath.row)})
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
NSLog("%d 행을 눌렀음",indexPath.row)
var bookInfo = BookAPIresult()
let row = self.list[indexPath.row]
bookInfo.title = row.title
bookInfo.author = row.author
bookInfo.thumbnail = row.thumbnail
bookInfo.pubnm = row.pubnm
bookInfo.listPrice = row.listPrice
bookInfo.sellPrice = ""
bookInfo.seller = "nobody"
bookInfo.description = row.description
//detailVeiw instance
let postInfo = self.storyboard?.instantiateViewControllerWithIdentifier("detailBook") as! detailBook
postInfo.navigationItem.title = bookInfo.title
postInfo.bookDetail.append(bookInfo)
self.navigationController?.pushViewController(postInfo, animated: true)
}
override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
self.view.endEditing(false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func callBookAPI(){
let encodedSearchString = searchString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let apiURI = NSURL(string: "https://apis.daum.net/search/book?apikey=\(self.apiKey)&q=\(encodedSearchString!)&searchType=title&output=json")
let apidata : NSData? = NSData(contentsOfURL: apiURI!)
NSLog("API Result = %#", NSString(data: apidata!, encoding: NSUTF8StringEncoding)!)
do {
let data = try NSJSONSerialization.JSONObjectWithData(apidata!, options:[]) as! NSDictionary
let channel = data["channel"] as! NSDictionary
// NSLog("\(data)")
let result = channel["item"] as! NSArray
var book : BookAPIresult
for row in result {
book = BookAPIresult()
let title = row["title"] as? String
book.title = title
if let authorEx = row["author"] as? String{
book.author = authorEx
}else{
book.author = ""
}
if let pubEX = row["pub_nm"] as? String{
book.pubnm = pubEX
}else{
book.pubnm = ""
}
if let listEX = row["list_price"] as? String{
book.listPrice = "\(listEX)dollar"
}else{
book.listPrice = "0"
}
if let thunmbEX = row["cover_s_url"] as? String{
book.thumbnail = thunmbEX
}else{
book.thumbnail = ""
}
//NSLog("\(book.thumbnail)")
if let description = row["description"] as? String{
if let decodedDescription = description.stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding){
book.description = decodedDescription
}else{
book.description = ""
}
}else{
book.description = ""
}
self.list.append(book)
}
} catch {
NSLog("parse error")
}
}
func getThumbnailImage(index : Int) -> UIImage {
let book = self.list[index]
if let savedImage = book.thumbnailImage {
return savedImage
} else {
if book.thumbnail == "" {
book.thumbnailImage = UIImage(named:
"Book Shelf-48.png")
}else{
let url = NSURL(string: book.thumbnail!)
let imageData = NSData(contentsOfURL: url!)
book.thumbnailImage = UIImage(data:imageData!)
}
return book.thumbnailImage!
}
}
func handleRefresh(refreshControl:UIRefreshControl){
self.searchString = self.searchController.searchBar.text!
self.list.removeAll()
self.callBookAPI()
self.tableView.reloadData()
refreshControl.endRefreshing()
}
override func prefersStatusBarHidden() -> Bool {
return false
}
}
extension SearchHome: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
// filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}
And the last is detailBook.
class detailBook : UITableViewController {
var bookDetail = Array<BookAPIresult>()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.navigationController?.hidesBarsOnTap = false
self.navigationController?.hidesBarsWhenVerticallyCompact = false
self.navigationController?.hidesBarsOnSwipe = false
self.navigationController?.navigationBarHidden = false
self.navigationItem.hidesBackButton = true
let backBtn = UIBarButtonItem(title: "뒤로가기", style: .Plain, target: self, action: "back:")
self.navigationItem.leftBarButtonItem = backBtn
//swipe to back
let backSwipe = UISwipeGestureRecognizer(target: self, action: "back:")
backSwipe.direction = UISwipeGestureRecognizerDirection.Right
self.view.addGestureRecognizer(backSwipe)
//dynamic cell height
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 620
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//define cell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! detailBookCell
let row = self.bookDetail[indexPath.row]
cell.author.text = row.author
cell.pubnm.text = row.pubnm
cell.listPrice.text = row.listPrice
cell.sellPrice.text = row.sellPrice
cell.detailInfo.text = row.description
cell.detailInfo.sizeToFit()
let url = NSURL(string: row.thumbnail!)
let imageData = NSData(contentsOfURL: url!)
cell.bookImage.image = UIImage(data:imageData!)
return cell
}
//back button
func back(recognizer: UISwipeGestureRecognizer){
self.navigationController?.popViewControllerAnimated(true)
bookDetail.removeAll()
}
}
You array, bookDetail, in the detailBlock class is still nil, so appending will not add any elements. You should first initialize a new array, add your bookInfo item to it, then assign detailBook's bookDetail item to this new array.
You'll need to use the prepareForSegue method in your search view controller and then use performSequeWithIdentifier in your didSelectRowAtIndexPath.
Basically, set up a placeholder object in the bookDetail view controller. In the search view controller set the value of a global object based in didSelecRowAtIndexPath and use the prepareForSegue method to set the placeholder object with the one you just set on your search view controller. When you select a row and it calls the performSegueWithIdentifier method, it will automatically call prepareForSegue and pass the value to the new view controller.

swift table section header duplicating on scroll

I am admitting defeat with these custom headers in Swift. I have tried for days to prevent the labels and images inside of the section headers from duplicating. Basically on scroll the labels/images inside the headers duplicate and lay on top of each other.
Would someone please for the love of god explain to me why this is happening and how to fix it.
The circular image keeps creating images and laying them on top of the previous, the same with the name and date labels!!
Here is my ViewController:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var navigationBar: UINavigationItem!
#IBOutlet weak var tableView: UITableView!
var list = []
var audioPlayer:AVAudioPlayer!
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getListBro() -> NSArray {
return list
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.dataSource = self;
tableView.delegate = self;
self.tableView.rowHeight = UITableViewAutomaticDimension
let streamURL = NSURL(string: "http://192.241.174.8:8000/beat-stream-all/")!
let stuff = GetBeatStream(url:streamURL)
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 50.0; //
stuff.downloadJSONFromURL {
(let jsonDictionary) in
if let jsonList = jsonDictionary["results"] as? NSArray {
self.list = jsonList
}
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
// do some task
dispatch_async(dispatch_get_main_queue()) {
// update some UI
self.tableView.reloadData()
}
}
}
}//end view did load
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("streamCell", forIndexPath: indexPath) as! StreamTableViewCell
cell.beatCover.image = UIImage(named: "imgres")
if let id = list[indexPath.section] as? NSDictionary {
if let beatID = id["id"] as? NSInteger {
cell.setID(beatID)
}
}
if let beat_cover = list[indexPath.section] as? NSDictionary {
if let beat_cover_image = beat_cover["beat_cover"] as? String {
cell.beatCover.downloadImageFrom(link: beat_cover_image, contentMode: UIViewContentMode.ScaleToFill) //set your image from link array.
}
}
if let audio = list[indexPath.section] as? NSDictionary {
if let audio_url = audio["audio"] as? String {
cell.url = audio_url
cell.buildPlayer()
}
}
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return list.count
}
func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let beatAuthorLabel = UILabel(frame: CGRectMake(55, 5, 200, 40))
//USER PIC
let profilePictureImageView = UIImageView(frame: CGRectMake(5, 5, 40, 40));
profilePictureImageView.layer.borderWidth = 0
profilePictureImageView.layer.masksToBounds = false
profilePictureImageView.layer.borderColor = UIColor.blackColor().CGColor
profilePictureImageView.layer.cornerRadius = profilePictureImageView.frame.height/2
profilePictureImageView.clipsToBounds = true
profilePictureImageView.layer.masksToBounds = true
profilePictureImageView.image = UIImage(named: "imgres") //set placeholder image first.
if let userPicSection = list[section] as? NSDictionary {
if let artist = userPicSection["artist"] as? NSDictionary {
if let profilePic = artist["profile_pic"] as? String {
profilePictureImageView.downloadImageFrom(link: profilePic, contentMode: UIViewContentMode.ScaleAspectFit)
}
}
}
if let nameSection = list[section] as? NSDictionary {
if let name = nameSection["artist"] as? NSDictionary {
if let adminName = name["admin_name"] as? NSString {
print(adminName)
beatAuthorLabel.text = adminName as String
beatAuthorLabel.font = UIFont(name: beatAuthorLabel.font.fontName, size: 14)
}
}
}
var dateLabel = UILabel(frame: CGRectMake(225, 5, 200, 40))
if let created = list[section] as? NSDictionary {
if let date = created["created_at"] as? String {
dateLabel.text = date as String
dateLabel.font = UIFont(name: dateLabel.font.fontName, size: 8)
}
}
let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
header.contentView.addSubview(beatAuthorLabel)
header.contentView.addSubview(dateLabel)
header.contentView.addSubview(dateLabel)
header.contentView.addSubview(profilePictureImageView)
header.contentView.backgroundColor = UIColor(red: 179/255, green: 194/255, blue: 191/255, alpha:1)
header.textLabel!.textColor = UIColor.whiteColor()
header.alpha = 1
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRowAtIndexPath(indexPath!)! as! StreamTableViewCell
currentCell.player.play()
}
func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let leavingCell = cell as! StreamTableViewCell
leavingCell.player.pause()
}
}
Header views, like cells, are reused. So, when the table view sends you tableView(_:willDisplayHeaderView:), it might not be the first time you've received that message for that header view. Yet every time you receive the message, you add four subviews to it.
Don't implement tableView(_:willDisplayHeaderView:forSection:) at all.
Instead, make a subclass of UITableViewHeaderFooterView with properties for the different subviews. This is just like how you created a subclass of UITableViewCell named StreamTableViewCell. Maybe you could call your header subview class StreamSectionHeaderView. Then you have two options for setting up the header view's subviews.
Option 1: In StreamSectionHeaderView.initWithFrame(_:), create the subviews of the header view (and store them in the instance properties and add them as subviews). This is essentially what you're doing now in tableView(_:willDisplayHeaderView:forSection:), but you would move most of the code into the StreamSectionHeaderView class. Register the StreamSectionHeaderView class with the table view using UITableView.registerClass(_:forHeaderFooterViewReuseIdentifier:).
Option 2: Design the header view and its subviews in a XIB (you can't do it in a storyboard), connect the subviews to the StreamSectionHeaderView properties (which must be IBOutlets in this case), and register the XIB in the table view with UITableView.registerNib(_:forHeaderFooterViewReuseIdentifier:).
To produce section, implement tableView(_:viewForHeaderInSection:) by calling tableView.
dequeueReusableHeaderFooterViewWithIdentifier(_:) and then configuring the header view's subviews, which already exist by the time dequeueReusableHeaderFooterViewWithIdentifier(_:) returns.
UPDATE
Here's your StreamSectionHeaderView, assuming you want to set up its subviews in code:
class StreamSectionHeaderView: UITableViewHeaderFooterView {
// Make these IBOutlets if you design StreamSectionHeaderView in a XIB.
var beatAuthorLabel = UILabel(frame: CGRectMake(55, 5, 200, 40))
var profilePictureImageView = UIImageView(frame: CGRectMake(5, 5, 40, 40))
var dateLabel = UILabel(frame: CGRectMake(225, 5, 200, 40))
init(frame: CGRect) {
profilePictureImageView.layer.borderWidth = 0
profilePictureImageView.layer.masksToBounds = false
profilePictureImageView.layer.borderColor = UIColor.blackColor().CGColor
profilePictureImageView.layer.cornerRadius = profilePictureImageView.frame.height/2
profilePictureImageView.clipsToBounds = true
profilePictureImageView.layer.masksToBounds = true
profilePictureImageView.image = UIImage(named: "imgres")
beatAuthorLabel.font = UIFont(name: beatAuthorLabel.font.fontName, size: 14)
dateLabel.font = UIFont(name: dateLabel.font.fontName, size: 8)
contentView.addSubview(beatAuthorLabel)
contentView.addSubview(dateLabel)
contentView.addSubview(profilePictureImageView)
contentView.backgroundColor = UIColor(red: 179/255, green: 194/255, blue: 191/255, alpha:1)
textLabel!.textColor = UIColor.whiteColor()
alpha = 1
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Then, in your table view controller:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
// blah blah blah other stuff
tableView.registerClass(StreamSectionHeaderView.self, forHeaderFooterViewReuseIdentifier: "Header")
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterViewWithIdentifier("Header") as! StreamSectionHeaderView
if let userPicSection = list[section] as? NSDictionary {
if let artist = userPicSection["artist"] as? NSDictionary {
if let profilePic = artist["profile_pic"] as? String {
header.profilePictureImageView.downloadImageFrom(link: profilePic, contentMode: UIViewContentMode.ScaleAspectFit)
}
}
}
if let nameSection = list[section] as? NSDictionary {
if let name = nameSection["artist"] as? NSDictionary {
if let adminName = name["admin_name"] as? NSString {
print(adminName)
header.beatAuthorLabel.text = adminName as String
}
}
}
if let created = list[section] as? NSDictionary {
if let date = created["created_at"] as? String {
header.dateLabel.text = date as String
}
}
return header
}
}

UIView inside UIView with TextField and Button not working

Good afternoon,
I'm trying to show a UIView when (in my case) there isn't any result to show in a tableView filled with products. When I detect 0 products, I show a UIView which contains a Label, a TextField and a Button, but I can't interact with my TextField and neither with the Button.
It's my first time using this technique to show a UIView when something went wrong with the tableView so I would like to know what's wrong in my code and what I'm missing because it's really weird.
Here is my code (when I print "Product not found" is where I show the UIView):
import UIKit
import Social
class ProductoCamViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var productoImageView:UIImageView!
#IBOutlet var tableView:UITableView!
#IBOutlet weak var noEncontrado:UIView!
var productoImage:String!
var ean:String!
var producto:Producto!
var productos = [Producto]()
#IBOutlet weak var toolBar: UIToolbar!
#IBOutlet weak var cargando: UIActivityIndicatorView!
override func viewDidLoad() {
toolBar.hidden = true
noEncontrado.hidden = true
cargando.hidden = false
super.viewDidLoad()
// Set table view background color
self.tableView.backgroundColor = UIColor(red: 240.0/255.0, green: 240.0/255.0, blue: 240.0/255.0, alpha: 0.2)
// Remove extra separator
self.tableView.tableFooterView = UIView(frame: CGRectZero)
// Change separator color
self.tableView.separatorColor = UIColor(red: 240.0/255.0, green: 240.0/255.0, blue: 240.0/255.0, alpha: 0.8)
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 88.0
requestPost()
cargando.hidden = true
tableView.reloadData()
}
override func viewDidAppear(animated: Bool) {
tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.hidesBarsOnSwipe = false
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func requestPost () {
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.mywebsite.com/product.php")!)
request.HTTPMethod = "POST"
let postString = "ean="+ean
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
// JSON RESULTADO ENTERO
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)!
if (responseString == "Product not found")
{
self.noEncontrado.hidden = false
self.tableView.reloadData()
return
}
else
{
self.productos = self.parseJsonData(data!)
self.toolBar.hidden = false
// Reload table view
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
}
task.resume()
}
func parseJsonData(data: NSData) -> [Producto] {
var productos = [Producto]()
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
noEncontrado.hidden = true
// Parse JSON data
let jsonProductos = jsonResult?["lista_productos"] as! [AnyObject]
for jsonProducto in jsonProductos {
let producto = Producto()
producto.imagen = jsonProducto["imagen"] as! String
producto.nombre = jsonProducto["nombre"] as! String
producto.descripcion = jsonProducto["descripcion"] as! String
producto.modo_de_empleo = jsonProducto["modo_de_empleo"] as! String
producto.marca = jsonProducto["marca"] as! String
producto.linea = jsonProducto["linea"] as! String
producto.distribuidor = jsonProducto["distribuidor"] as! String
producto.tamano = jsonProducto["tamano"] as! String
producto.precio = jsonProducto["precio"] as! String
producto.codigo_nacional = jsonProducto["codigo_nacional"] as! String
productos.append(producto)
}
}
catch let parseError {
print(parseError)
}
return productos
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return productos.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
title = productos[indexPath.row].nombre
let cell = tableView.dequeueReusableCellWithIdentifier("CellDetail", forIndexPath: indexPath) as! ProductoTableViewCell
cell.selectionStyle = .None
if let url = NSURL(string: productos[indexPath.row].imagen) {
if let data = NSData(contentsOfURL: url) {
self.productoImageView.image = UIImage(data: data)
}
}
cell.nombre.text = productos[indexPath.row].nombre
cell.descripcion.text = productos[indexPath.row].descripcion
cell.modo_de_empleo.text = productos[indexPath.row].modo_de_empleo
cell.marca.text = productos[indexPath.row].marca
cell.linea.text = productos[indexPath.row].linea
cell.distribuidor.text = productos[indexPath.row].distribuidor
cell.tamano.text = productos[indexPath.row].tamano
cell.precio.text = productos[indexPath.row].precio
cell.codigo_nacional.text = productos[indexPath.row].codigo_nacional
cell.layoutIfNeeded()
return cell
}
}
Thanks in advance.
At first, please try to provide english code :) but anyways. I think the view what should appear is nonEncontrado.
There could be some issues but i need to see the storyboard.
The view has userInteraction not enabled. Its a property and can also be activated in the storyboard
The view is overlayed by something else. Maybe the empty tableView.
As an suggestion you could provide this fields in the tableView and just load another DataSource. Than you dont need to fight with extra views. If you provide screens from the Storyboard i could help a bit more.
Good Luck :)

Resources