Table view scrolling is erratic and jumpy even though I reuse cells? - ios

Okay, so I'm not sure whats been happening with my Table view, but it seems to act a bit strange now that I load images from parse onto it. At first, it ran smoothly, but now that I'm working in ios 9, the scrolling is horrible
Optimizations I've used to reduce this(Keep in mind they do not really help.)
-Removed transparent objects and set them to default background
-reused table cells
-Tried to use lower quality images
Here is my code
import UIKit
class mainVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var resultsTable: UITableView!
#IBOutlet weak var menuButton:UIBarButtonItem!
var deleteArray = [String]()
var followArray = [String]()
var resultsLocationArray = [String]()
var datetextfielArray = [String]()
var imageDates = [String]()
var resultsNameArray = [String]()
var resulltsImageFiles = [PFFile]()
var resultsTweetArray = [String]()
var resultsHasImageArray = [String]()
var resultsTweetImageFiles = [PFFile?]()
var refresher:UIRefreshControl!
override func viewDidLoad() {
super.viewDidLoad()
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
// Uncomment to change the width of menu
//self.revealViewController().rearViewRevealWidth = 62
}
let theWidth = view.frame.size.width
let theHeight = view.frame.size.height
resultsTable.frame = CGRectMake(0, 0, theWidth, theHeight)
let tweetBtn = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Compose, target: self, action: Selector("tweetBtn_click"))
let searchBtn = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Search, target: self, action: Selector("searchBtn_click"))
let buttonArray = NSArray(objects: tweetBtn, searchBtn)
self.navigationItem.rightBarButtonItems = buttonArray as? [UIBarButtonItem]
refresher = UIRefreshControl()
refresher.tintColor = UIColor.blackColor()
refresher.addTarget(self, action: "refresh", forControlEvents: UIControlEvents.ValueChanged)
self.resultsTable.addSubview(refresher)
}
func refresh() {
print("refresh table")
refreshResults()
}
func refreshResults() {
followArray.removeAll(keepCapacity: false)
resultsNameArray.removeAll(keepCapacity: false)
resulltsImageFiles.removeAll(keepCapacity: false)
resultsTweetArray.removeAll(keepCapacity: false)
resultsLocationArray.removeAll(keepCapacity: false)
resultsHasImageArray.removeAll(keepCapacity: false)
resultsTweetImageFiles.removeAll(keepCapacity: false)
datetextfielArray.removeAll(keepCapacity: false)
let followQuery = PFQuery(className: "follow")
followQuery.whereKey("user", equalTo: PFUser.currentUser()!.username!)
followQuery.addDescendingOrder("createdAt")
let objects = followQuery.findObjects()
for object in objects! {
self.followArray.append(object.objectForKey("userToFollow") as! String)
}
let query:PFQuery = PFQuery(className: "tweets")
query.whereKey("userName", containedIn: followArray)
query.addDescendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil {
for object in objects! {
self.resultsNameArray.append(object.objectForKey("profileName") as! String)
self.resulltsImageFiles.append(object.objectForKey("photo") as! PFFile)
self.resultsTweetArray.append(object.objectForKey("tweet") as! String)
//resultsLocationArray
self.resultsLocationArray.append(object.objectForKey("tweetlocation") as! String)
self.resultsHasImageArray.append(object.objectForKey("hasImage") as! String)
self.resultsTweetImageFiles.append(object.objectForKey("tweetImage") as? PFFile)
self.datetextfielArray.append(object.objectForKey("datetextfield") as! String)
self.resultsTable.reloadData()
}
self.refresher.endRefreshing()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
self.navigationController?.navigationBarHidden = false
super.viewWillAppear(animated)
let nav = self.navigationController?.navigationBar
nav?.barStyle = UIBarStyle.Black
nav?.tintColor = UIColor.whiteColor()
nav?.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
self.navigationItem.hidesBackButton = true
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func viewDidAppear(animated: Bool) {
refreshResults()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return resultsNameArray.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if resultsHasImageArray[indexPath.row] == "yes" {
return self.view.frame.size.width + 130
} else {
return 130
}
}
//var theDtS = dtFormater.stringFromDate(self.dateArray[i])
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:mainCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! mainCell
cell.tweetImg.hidden = true
cell.locationTxt.text = self.resultsLocationArray[indexPath.row]
cell.profileLbl.text = self.resultsNameArray[indexPath.row]
cell.messageTxt.text = self.resultsTweetArray[indexPath.row]
cell.datetextfield.text = self.datetextfielArray[indexPath.row]
resulltsImageFiles[indexPath.row].getDataInBackgroundWithBlock {
(imageData:NSData?, error:NSError?) -> Void in
//resultsLocationArray
if error == nil {
let image = UIImage(data: imageData!)
cell.imgView.image = image
}
}
if resultsHasImageArray[indexPath.row] == "yes" {
let theWidth = view.frame.size.width
cell.tweetImg.frame = CGRectMake(0, 0, theWidth, theWidth)
cell.tweetImg.hidden = false
resultsTweetImageFiles[indexPath.row]?.getDataInBackgroundWithBlock({
(imageData:NSData?, error:NSError?) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
cell.tweetImg.image = image
}
})
}
return cell
}
func tweetBtn_click() {
print("tweet pressed")
self.performSegueWithIdentifier("gotoTweetVCFromMainVC", sender: self)
}
func searchBtn_click() {
print("search pressed")
self.performSegueWithIdentifier("gotoUsersVCFromMainVC", sender: self)
}
}

Related

Swift, loading more data into table view causes scroll lags

I have a UITableView with data, that parsed from url.
UITableView will load more data, when scrolled right to the bottom (or got some more space to scroll, but close to the end - did both, same results)
When more data is loaded - i simple append it to array of my class, that contains data for TableView and then list scrolls back more than half of list (e.g. got 40 items, loading 10 more -> scrolling back to 20-25).
Calling TableView.reloadData() after append is complete.
Is there some mistakes in plan of doing it?
I can share code, but it's pretty common.
class TabAllTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, XMLParserDelegate {
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var BlogAllTableView: UITableView!
var loadMoreStatus = false
var maxPage = 1.0
var currentPageLoad = 1.0
var blogList: [DetailAndRenderBlogObject] = []
var eName: String = String()
var category = String()
var full_text = String()
var short_text = String()
var blog_title = String()
var avatar = String()
var full_name = String()
var url = String()
var created_at = String()
private let CATEGORY = ConfigData.CATEGORY_ALL
let cellIdentifier = "BlogTableViewCell"
override func viewDidLoad() {
super.viewDidLoad()
setupNavMenuButtons()
self.BlogAllTableView.insertSubview(refreshControl, at: 0)
self.BlogAllTableView.tableFooterView?.isHidden = true
downloadBlogData(1, true, true)
BlogAllTableView.delegate = self
BlogAllTableView.dataSource = self
}
var refreshControl: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action:
#selector(TabAllTableViewController.handleRefresh(_:)),
for: UIControlEvents.valueChanged)
refreshControl.tintColor = Colors.ColorLoadingIndicator
return refreshControl
}()
#objc func handleRefresh(_ refreshControl: UIRefreshControl) {
downloadBlogData(1, true, false)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
let deltaOffset = maximumOffset - currentOffset
if deltaOffset <= 0 && currentPageLoad < maxPage {
loadMore()
}
}
func loadMore() {
if ( !loadMoreStatus ) {
self.loadMoreStatus = true
self.activityIndicator.startAnimating()
self.BlogAllTableView.tableFooterView?.isHidden = false
downloadBlogData(currentPageLoad, false, false)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.sideMenuController?.isLeftViewEnabled = true
AppDelegate.tabBarReference.isHidden = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.navigationBar.topItem?.title = "all".localized()
}
private func setupNavMenuButtons() {
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(handleMenuRefresh))
let image = UIImage(named:"menu_ham.png")
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
imageView.contentMode = .scaleAspectFit
imageView.isUserInteractionEnabled = true
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.image = image
let imageHeight = navigationController?.navigationBar.frame.size.height
let wrapperView = UIView(frame: CGRect(x: 0, y: 0, width: imageHeight!, height: imageHeight!))
wrapperView.addSubview(imageView)
imageView.center = CGPoint(x: imageView.frame.size.width / 2, y: wrapperView.frame.size.height / 2)
let tap = UITapGestureRecognizer(target: self, action: #selector(TabAllTableViewController.menuButtonClick))
wrapperView.addGestureRecognizer(tap)
let btnHamburgerMenu: UIBarButtonItem = UIBarButtonItem(customView: wrapperView)
navigationItem.setLeftBarButton(btnHamburgerMenu, animated: false)
}
#objc private func menuButtonClick()
{
self.sideMenuController?.showLeftViewAnimated()
}
#objc private func handleMenuRefresh() {
downloadBlogData(1, true, true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return blogList.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if (section != 0) {
return 12
} else {
return CGFloat.leastNonzeroMagnitude
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor.black.withAlphaComponent(0.0)
return view
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return CGFloat(ConfigData.CELL_HEIGHT)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell...
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BlogTableViewCell else {
fatalError("The dequeued cell is not an instance of BlogTableViewCell.")
}
cell.layer.cornerRadius = 10
let blogItem = blogList[indexPath.section]
cell.avatarBackground.contentMode = .scaleAspectFill
cell.avatarBackground.layer.cornerRadius = cell.avatarBackground.frame.size.width / 2;
cell.avatarBackground.clipsToBounds = true;
if let authorImgUrl = URL(string: blogItem.authorImg) {
cell.authorRoundImage.contentMode = .scaleAspectFill
cell.authorRoundImage.layer.cornerRadius = cell.authorRoundImage.frame.size.width / 2;
cell.authorRoundImage.clipsToBounds = true;
//Helper.downloadImage(url: authorImgUrl, imageview: cell.authorRoundImage)
cell.authorRoundImage.sd_setImage(with: authorImgUrl)
}
if let headerImageUrl = URL(string: blogItem.image) {
cell.bigImage.contentMode = .scaleToFill
//Helper.downloadImage(url: headerImageUrl, imageview: cell.bigImage)
cell.bigImage.sd_setImage(with: headerImageUrl)
}
cell.authorLabel.text = blogItem.author
cell.dateLabel.text = blogItem.date
cell.title.text = blogItem.title
cell.titleDescription.text = blogItem.shortDescription
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let secondViewController = self.storyboard!.instantiateViewController(withIdentifier: "ShowArticleViewController") as! ShowArticleViewController
secondViewController.blogItem = blogList[indexPath.section]
self.navigationController!.pushViewController(secondViewController, animated: true)
}
private func downloadBlogData(_ page : Double, _ refresh : Bool, _ showOverlay : Bool) {
// print(InAppProperties.sharedInstance.getDefaulLang())
if showOverlay {
LoadingOverlay.shared.showOverlay(view: self.view)
}
let req = NSMutableURLRequest(url: NSURL(string: Helper.getBlogUrl(CATEGORY, Int(page)))! as URL)
req.httpMethod = "GET"
req.httpBody = "key=\"value\"".data(using: String.Encoding.utf8) //This isn't for GET requests, but for POST requests so you would
URLSession.shared.dataTask(with: req as URLRequest) { data, response, error in
if error != nil {
//Your HTTP request failed.
print(error?.localizedDescription)
} else {
//Your HTTP request succeeded
if refresh {
self.currentPageLoad = 1
} else {
self.currentPageLoad += 1
}
let stringData = String(data: data!, encoding: String.Encoding.utf8)!
//print(stringData)
let xml = SWXMLHash.parse(stringData)
if page == 1 {
self.blogList = [DetailAndRenderBlogObject]()
}
for elem in xml["response"]["provider"].all {
let itemList = elem["item"]
for article in itemList.all {
let blogItem = DetailAndRenderBlogObject()
blogItem.articleUrl = article["url"].element!.text
blogItem.author = article["full_name"].element!.text
blogItem.authorImg = article["avatar"].element!.text
blogItem.text = article["full_text"].element!.text
blogItem.shortDescription = article["short_text"].element!.text
blogItem.title = article["title"].element!.text
blogItem.categoryUrl = article["category"].element!.text
blogItem.image = self.repairLink(article["thumbnail"].element!.text)
blogItem.date = self.formatDate(article["created_at"].element!.text)
if (blogItem.categoryUrl.lowercased().range(of:"video") == nil &&
blogItem.title.lowercased().range(of: "видео") == nil) {
self.blogList.append(blogItem)
}
}
if let totalItemsCount = xml["response"]["pagination"]["totalCount"].element?.text {
self.maxPage = (Double(totalItemsCount)! / 10).rounded(.up)
}
}
DispatchQueue.main.async {
self.BlogAllTableView.reloadData()
self.refreshControl.endRefreshing()
self.loadMoreStatus = false
self.activityIndicator.stopAnimating()
self.BlogAllTableView.tableFooterView?.isHidden = true
if showOverlay {
LoadingOverlay.shared.hideOverlayView()
}
if (page == 1) {
let indexPath = NSIndexPath(row: 0, section: 0)
self.BlogAllTableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)
}
}
}
}.resume()
}
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
eName = elementName
if elementName == "item" {
category = String()
full_text = String()
short_text = String()
blog_title = String()
avatar = String()
full_name = String()
url = String()
created_at = String()
}
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "item" {
let blogListItem = DetailAndRenderBlogObject()
blogListItem.categoryUrl = category
blogListItem.text = full_text
blogListItem.shortDescription = short_text
blogListItem.title = blog_title
blogListItem.authorImg = avatar
blogListItem.author = full_name
blogListItem.articleUrl = url
blogListItem.date = created_at
print("WRITING DATA")
blogList.append(blogListItem)
}
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
//print("ENAME: " + eName)
switch eName {
case "category":
print("writing category: " + string)
category = string;
case "full_text":
full_text = string;
case "short_text":
short_text = string;
case "title":
title = string;
case "thumbnail":
avatar = string;
case "avatar":
avatar = string;
case "full_name":
full_name = string;
case "url":
url = string;
case "created_at":
created_at = string;
default:
()
}
}
func parserDidEndDocument(_ parser: XMLParser) {
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print(parseError)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func repairLink(_ link : String) -> String { //sometimes links looks like https://asdasd.comhttps://asdasd.com/...
let newLink = link.suffix(from: link.index(link.startIndex, offsetBy: 1))
if let range = newLink.range(of: "http") {
let substring = newLink[range.lowerBound...]
return String(substring)
}
return link
}
func formatDate(_ date : String) -> String { //date String "2018-01-22 08:59:43"
if let dividerIndex = date.index(of: " ") {
return String(date[..<dividerIndex])
}
return date
}
}
Reloading the entire table is quite an intensive operation. The better way might be to use the tableView.insertRows(at:with:) method. An example would be something like -
func didFinishLoadingData(newBlogs: [DetailAndRenderBlogObject]) {
tableView.beginUpdates()
var indexPaths: [IndexPath] = []
for row in (blogList.count..<(blogList.count + newBlogs.count)) {
indexPaths.append(IndexPath(row: row, section: 0))
}
blogList.append(contentsOf: newBlogs)
tableView.insertRows(at: indexPaths, with: .fade)
tableView.endUpdates()
}
I had the same issue and found another solution, after long time search.
It seems that whenever you call the endRefreshing() function of an UIRefreshControl that is attached to a UITableView scrollview, it stops the decelerating process of the scroll view.
So, if your code, that fetches the next page, is calling endRefreshing() function (with no reason at all) and the user started the scrolling and lifted his/her finger from the device's screen (scrollview is decelerating) this will have as a result to stop the scrolling.
The solution is simple, replace this line in your code:
self.refreshControl.endRefreshing()
with these lines:
if self.refreshControl.isRefreshing {
self.refreshControl.endRefreshing()
}
I even created a handy extension that does this check for me, to call it whenever I need it:
extension UIRefreshControl {
func endIfRefreshing() {
if isRefreshing { endRefreshing() }
}
}

Filtering UItableview Swift IOS

I am trying to filter a UITableView based on variables that have been passed back from another VC which is a Eureka form handling the filter UI.
I would like to filter the tableview based on these two variables :
var filterByPrice: Float?
var filteredRentalTypes: Set<String>?
I have got the price filter working but I am having trouble filtering with the rental type. There may be a more efficient way of doing this but this is my code so far. With my current code I get an 'index out of range' crash for the rental type filter.
This is my TableViewVC:
class RentalTableViewVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
var rentalsArray = [Rental]()
var filteredArrary = [Rental]()
var myRentals = [String]()
var filterByPrice: Float?
var filteredRentalTypes: Set<String>?
static var imageCache: NSCache<NSString, UIImage> = NSCache()
#IBOutlet weak var tableView: UITableView!
var hidingBarMangar: HidingNavigationBarManager?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toDetailVC" {
let destination = segue.destination as? DetailVC
let value = tableView.indexPathForSelectedRow?.row
if filteredArrary.count != 0 {
destination?.emailAdress = filteredArrary[value!].email!
destination?.bond = filteredArrary[value!].bond!
destination?.dateAval = filteredArrary[value!].dateAval!
destination?.pets = filteredArrary[value!].pets!
destination?.rent = filteredArrary[value!].price!
destination?.rentalTitle = filteredArrary[value!].title!
destination?.imageURL = filteredArrary[value!].imageURL!
destination?.des = filteredArrary[value!].description!
destination?.rentalType = filteredArrary[value!].rentalType!
destination?.streetName = filteredArrary[value!].streetName!
destination?.city = filteredArrary[value!].city!
destination?.postcode = filteredArrary[value!].postcode!
} else {
destination?.emailAdress = rentalsArray[value!].email!
destination?.bond = rentalsArray[value!].bond!
destination?.dateAval = rentalsArray[value!].dateAval!
destination?.pets = rentalsArray[value!].pets!
destination?.rent = rentalsArray[value!].price!
destination?.rentalTitle = rentalsArray[value!].title!
destination?.imageURL = rentalsArray[value!].imageURL!
destination?.des = rentalsArray[value!].description!
destination?.rentalType = rentalsArray[value!].rentalType!
destination?.streetName = rentalsArray[value!].streetName!
destination?.city = rentalsArray[value!].city!
destination?.postcode = rentalsArray[value!].postcode!
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if filteredArrary.count != 0 {
return filteredArrary.count
} else {
return rentalsArray.count
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var rental = rentalsArray[indexPath.row]
if self.filteredArrary.count != 0 {
rental = filteredArrary[indexPath.row]
}
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? RentalCell {
var rentalImage = ""
if rental.imageURL != nil {
rentalImage = rental.imageURL!
}
if let img = RentalTableViewVC.imageCache.object(forKey: rentalImage as NSString) {
cell.configureCell(rental: rental, image: img)
return cell
} else {
cell.configureCell(rental: rental, image: nil)
return cell
}
} else {
return RentalCell()
}
}
#IBAction func backPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
hidingBarMangar?.viewWillAppear(animated)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
hidingBarMangar?.viewDidLayoutSubviews()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
hidingBarMangar?.viewWillDisappear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.dataSource = self
//Firebase observer
DataService.ds.DBrefRentals.observe(.value) { (snapshot) in
self.rentalsArray = []
self.filteredArrary = []
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
if let dicOfRentals = snap.value as? Dictionary<String,AnyObject> {
let key = snap.key
let rental = Rental(postID: key, userData: dicOfRentals)
self.rentalsArray.append(rental)
//Placing filtered items in the filtered array
if self.filterByPrice != nil {
let priceAsFloat = (rental.price! as NSString).floatValue
if self.filterByPrice! >= priceAsFloat {
self.filteredArrary.append(rental)
}
}
if self.filteredRentalTypes != nil {
for rentals in self.filteredRentalTypes! {
if rental.rentalType == rentals {
print("******hh\(String(describing: self.filteredRentalTypes))")
self.filteredArrary.append(rental)
}
}
}
}
}
self.tableView.reloadData()
}
}
addHidingBar()
}
override func viewDidAppear(_ animated: Bool) {
print("**********\(String(describing: filterByPrice)))")
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
rentalsArray[indexPath.row].incrimentViews()
let postViewsToFB = DataService.ds.DBrefRentals.child(rentalsArray[indexPath.row].postID!)
postViewsToFB.child("views").setValue(rentalsArray[indexPath.row].views)
performSegue(withIdentifier: "toDetailVC" , sender: nil)
}
func addHidingBar() {
let extensionView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 40))
extensionView.layer.borderColor = UIColor.lightGray.cgColor
extensionView.layer.borderWidth = 1
extensionView.backgroundColor = UIColor(white: 230/255, alpha: 1)
/*let label = UILabel(frame: extensionView.frame)
label.text = "Extension View"
label.textAlignment = NSTextAlignment.center
extensionView.addSubview(label) */
let btn = UIButton(frame: CGRect(x: 20, y: 15, width: 75, height: 10))
btn.setTitle("Filter", for: .normal)
let btnColour = UIColor(displayP3Red: 0.0, green: 122.0/255.0, blue: 1.0, alpha: 1.0)
btn.setTitleColor(btnColour, for: .normal)
btn.titleLabel?.font = headerFont
btn.addTarget(self, action: #selector(filterBtnPressed), for: .touchUpInside)
extensionView.addSubview(btn)
hidingBarMangar = HidingNavigationBarManager(viewController: self, scrollView: tableView)
hidingBarMangar?.addExtensionView(extensionView)
}
#objc func filterBtnPressed() {
performSegue(withIdentifier: "toFilterVC", sender: nil)
}
}
You may return different array in numberOfRows and their sizes may be different so check count before indexing any array as if array count != 0 doesn't mean you can index it with index path.row that may be greater then 0 ,Change this line in cellForRow
if self.filteredArrary.count != 0 {
rental = filteredArrary[indexPath.row]
}
to
if indexPath.row < self.filteredArrary.count {
rental = filteredArrary[indexPath.row]
}

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.

Adding Searchbar to table view returns unwrapping nil view

Im attempting to add a search bar to a table view that displays names. Im getting a unwrapping nil error when i try to add it as a subheader to my tableview. Im not sure why. The point where the error occurs is marked.
import UIKit
class WebpageController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchResultsUpdating {
var names: [String] = [String]()
var school: String = String()
var index: Int = Int()
var searchResults: [String] = [String]()
var searchController = UISearchController(searchResultsController: nil)
#IBOutlet weak var tables: UITableView!
var refreshControl: UIRefreshControl!
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
definesPresentationContext = true
tables.tableHeaderView = searchController.searchBar //returns error
names = HtmlController.loadData() as NSArray as! [String]
clean()
refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(WebpageController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
tables.addSubview(refreshControl)
// Do any additional setup after loading the view.
}
func refresh(sender: AnyObject)
{
names = HtmlController.loadData() as NSArray as! [String]
clean()
tables.reloadData()
refreshControl.endRefreshing()
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchText = searchController.searchBar.text
filterContentForSearchText(searchText!)
tables.reloadData()
}
func filterContentForSearchText(searchText: String)
{
if(searchText == "")
{
searchResults = names
}
else{
searchResults = names.filter({ ( a: String) -> Bool in
let nameMatch = a.rangeOfString(searchText, options:
NSStringCompareOptions.CaseInsensitiveSearch)
return nameMatch != nil
})
}
}
#IBOutlet weak var Table: UITableView!
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func clean()
{
var length = names.count
var i = 0;
var bool = false
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("School") == nil
{
school = "11Staff"
defaults.setObject("11", forKey: "School")
}
else{
school = (defaults.objectForKey("School") as! String) + "Staff"
}
var extra: [String] = [String]()
let bundleidentifier = NSBundle.mainBundle().bundleIdentifier
if let aStreamReader = StreamReader(path: NSBundle.mainBundle().pathForResource(school, ofType: "txt")!)
{
defer {
aStreamReader.close()
}
while let line = aStreamReader.nextLine() {
extra.append(line)
}
}
for String in extra
{
while i < length && bool == false
{
if((String.rangeOfString(names[i].uppercaseString)) != nil)
{
names.removeAtIndex(i)
i -= 1
bool = true
length = names.count
}
i+=1;
}
bool = false
i = 0;
}
}
func numberOfSectionsInTableView(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
if searchController.active
{
return searchResults.count
}
else{
return names.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell
if let reusedCell = tableView.dequeueReusableCellWithIdentifier("Cell") {
cell = reusedCell
} else {
cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell")
}
if let label = cell.textLabel {
label.text = names[indexPath.row + 1].uppercaseString
}
return cell }
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
index = indexPath.row
performSegueWithIdentifier("WebTransfer", sender:self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let Destination: WebsiteController = segue.destinationViewController as! WebsiteController
Destination.index = index
}
}

Swift Use of Unresolved Identifier 'IndexPath' TableView Cell Button

I am attempting to get the text of my UILabel and set it to my Parse object, but I am running into an issue setting the object to the index path of the cell. I am getting an Use of unresolved identifier 'indexPath' error at that line.
follow["following"] = self.userArray.objectAtIndex(IndexPath.row)
Here is my tableview controller:
import UIKit
class SearchUsersRegistrationTableViewController: UITableViewController {
var userArray : NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
var user = PFUser.currentUser()
loadParseData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: SearchUsersRegistrationTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SearchUsersRegistrationTableViewCell
let row = indexPath.row
var individualUser = userArray[row] as! PFUser
var username = individualUser.username as String
var profileImage = individualUser["profileImage"] as? PFFile
if profileImage != nil {
profileImage!.getDataInBackgroundWithBlock({
(result, error) in
cell.userImage.image = UIImage(data: result)
})
} else {
cell.userImage.image = UIImage(named: "profileImagePlaceHolder")
}
cell.usernameLabel.text = username
cell.addUserButton.tag = row
cell.addUserButton.addTarget(self, action: "addUser:", forControlEvents: .TouchUpInside)
return cell
}
func loadParseData() {
var query : PFQuery = PFUser.query()
query.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]!, error:NSError!) -> Void in
if error == nil {
if let objects = objects {
println("\(objects.count) users are listed")
for object in objects {
self.userArray.addObject(object)
}
self.tableView.reloadData()
}
} else {
println("There is an error")
}
}
}
#IBAction func addUser(sender: UIButton) {
println("Button Triggered")
let addUserButton : UIButton = sender
let user : PFObject = self.userArray.objectAtIndex(addUserButton.tag) as! PFObject
var follow = PFObject(className: "Follow")
follow["following"] = self.userArray.objectAtIndex(IndexPath.row)
follow["follower"] = PFUser.currentUser().username
follow.saveInBackground()
}
}
Here is my tableview cell:
import UIKit
class SearchUsersRegistrationTableViewCell: UITableViewCell {
#IBOutlet weak var userImage: UIImageView!
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var addUserButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
userImage.layer.borderWidth = 1
userImage.layer.masksToBounds = false
userImage.layer.borderColor = UIColor.whiteColor().CGColor
userImage.layer.cornerRadius = userImage.frame.width/2
userImage.clipsToBounds = true
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Try this
follow["following"] = self.userArray.objectAtIndex(sender.tag)
You are setting the row as tag for your button. Just use it.
you should not work with tags in that case.
to get the indexpath in your addUser function add the following:
let indexPath = tableView.indexPathForRowAtPoint(addUserButton.convertPoint(CGPointZero, toView: tableView))
then you can use that line:
follow["following"] = self.userArray.objectAtIndex(indexPath.row)
indexPath, not IndexPath

Resources