Tableview Pagination not working in swift - json

I have added tableview bottom pagination with json data
json response in postman:
"result": {
"page_count": 3,
"per_page": 10,
"post_count": 27,
"Payments": [
{
"id": 132,
"payment_response": null,
code: if i run this code then without scrolling the paymentsServiceCall() calling 4 times but table data showing only first 10 records, why? where am i wrong.
i need initially 10 cells to show on tableview then if i scroll then 10 more cells to load and so on. please guide me
class MyPaymentsViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
private var currentPage = 1
private var totalPages = 1
private var paymentsData = PaymentsHistoryModel(dictionary: NSDictionary()) {
didSet {
totalPages = paymentsData?.result?.page_count ?? 0
tableView.reloadData()
}
}
override func viewWillAppear(_ animated: Bool) {
currentPage = 1
paymentsServiceCall()
}
func paymentsServiceCall() {
let param = ["page_no": currentPage]
serviceCall(param: param, method: .post, url: CommonUrl.student_my_payments) { [weak self] (resp) in
if let _ = resp.dict?["result"] as? NSDictionary {
self?.paymentsData = PaymentsHistoryModel(dictionary: resp.dict as NSDictionary? ?? NSDictionary())
}
}
}
}
extension MyPaymentsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let totalPostCount = paymentsData?.result?.payments?.count
if indexPath.row == totalPostCount - 1, currentPage <= totalPages {
print("in refresh")
currentPage += 1
paymentsServiceCall()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
paymentsData?.result?.payments?.count ?? 0
}
}

Try Using This
class PaginationManager {
var isLoading = false
static let shared = PaginationManager()
private init() { } //Singleton
}
extension PaginationManager {
func isPaging(_ scrollView: UIScrollView) -> Bool {
let offset = scrollView.contentOffset
let bounds = scrollView.bounds
let size = scrollView.contentSize
let inset = scrollView.contentInset
let y = offset.y + bounds.size.height - inset.bottom
let h = size.height
let reload_distance: CGFloat = 100.0
if y > h - reload_distance && !isLoading {
return true
}
return false
}
func isPagingForTopScroll(_ scrollView: UIScrollView) -> Bool {
let offset = scrollView.contentOffset
_ = scrollView.bounds
let size = scrollView.contentSize
_ = scrollView.contentInset
let y = offset.y
_ = size.height
let reload_distance: CGFloat = 100.0
if y < reload_distance && !isLoading {
return true
}
return false
}
}
And for using it in Controller
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if PaginationManager.shared.isPaging(scrollView) {
if !(paymentsData?.result?.payments?.isEmpty ?? true), totalPages > paymentsData?.result?.payments?.count ?? 0 {
guard PaginationManager.shared.isLoading else { return }
track("######Loading: \(viewModel.isLoading)")
//Call your service to fetch new data with an updated page number below
}
}
}
After calling the service again check for appending the data in payments or if it is the first page then replace it with new data and inside the service response update the current page count by 1
You can also create isLoading var in your class if multiple service calls are there.

Related

Open pdf in tableview from a json file

I am in a bind.
My regular developer has secured a full time gig, and I am trying to finish a project.
Literally stuck on the last line of code.....
I have a tableview that parses a json file to populate and open a pdf file when a row is selected.
I reworked the tableview in order to add a searchbar and associated code, which works when running the bundle.
I am, however, stuck with connecting the didSelectRow with the appropriate pdf file.
Can someone please lend a hand? Thanks
import UIKit
import WebKit
// Model
struct Section {
var section: String
var pdfs: [PDF]
}
struct PDF {
var code: String
var name: String
var airport: String
}
class PdfVC: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var webView: WKWebView!
#IBOutlet weak var tableViewLeft: NSLayoutConstraint!
#IBOutlet weak var btnMenuLeft: NSLayoutConstraint!
#IBOutlet weak var btnMenu: UIButton!
#IBOutlet weak var searchBar: UISearchBar!
//json
var jsonData: [Section] = []
var filtedJsonData: [Section] = []
//WebView
var fileName: String?
var isVideo: Bool = false
var btnMenuClose:UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
hideKeyboardWhenTappedAround()
// Setup TableView
tableView.delegate = self
tableView.dataSource = self
setupData()
tableView.reloadData()
// Setup SearchBar
searchBar.delegate = self
// Setup WebView
if let fileName = fileName {
isVideo = fileName.contains(".mp4")
if let range = fileName.range(of: isVideo ? ".mp4" : ".pdf") {
self.fileName!.removeSubrange(range)
}
}
if let file = Bundle.main.url(forResource: fileName ?? "", withExtension: isVideo ? ".mp4" : ".pdf", subdirectory: nil, localization: nil) {
let request = URLRequest(url: file)
webView.load(request)
}
if let image = UIImage(named: "ico_close") {
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(onWebTap))
//self.navigationItem.leftItemsSupplementBackButton = true
btnMenuClose = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(onWebTap))
self.navigationItem.rightBarButtonItems = [btnMenuClose!];
}
//navigationItem.title = "Airport Maps"
}
private func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc private func dismissKeyboard() {
view.endEditing(true)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(false, animated: animated)
// title color to white
let titleTextAttributed: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white, .font: UIFont(name: "HelveticaNeue-Light", size: 20) as Any]
self.navigationController?.navigationBar.titleTextAttributes = titleTextAttributed
// back arrow white
self.navigationController?.navigationBar.tintColor = UIColor.white
// blueish navigation bar
self.navigationController?.navigationBar.barTintColor = UIColor(red: 0.00, green: 0.54, blue: 0.92, alpha: 1.00)
self.navigationController?.navigationBar.isTranslucent = false
showMenu(false, animated: false)
}
private func setupData() {
// Parse data from local json file
if let path = Bundle.main.path(forResource: "airports", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
if let jsonResult = jsonResult as? [Dictionary<String, String>] {
for result in jsonResult {
if !jsonData.contains(where: { $0.section == result["Section"] }) {
let section = Section(section: result["Section"] ?? "", pdfs: [])
jsonData.append(section)
}
let pdf = PDF(code: result["Code"] ?? "", name: result["Filename"] ?? "", airport: result["Name"] ?? "")
let index = jsonData.firstIndex(where: { $0.section == result["Section"] })
if let index = index {
jsonData[index].pdfs.append(pdf)
}
}
}
} catch {
// Handle error
print("error parsing json")
}
}
// Sort data before use
jsonData.sort(by: { $0.section < $1.section })
for index in 0..<jsonData.count {
jsonData[index].pdfs.sort(by: { $0.airport < $1.airport } )
}
filtedJsonData = jsonData
}
#objc func onWebTap() {
var left:CGFloat = 0
if tableViewLeft.constant == 0 {
left = -320
}
tableViewLeft.constant = left
showMenu(left != 0, animated: true)
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseInOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
func showMenu(_ show:Bool, animated:Bool) {
if let image = UIImage(named: show ? "ico_open" : "ico_close") {
btnMenuClose?.image = image
//navigationItem.rightBarButtonItem?.image = image
//}
//var left:CGFloat = 20
//if show == false {
// left = -64
}
//btnMenuLeft.constant = left
UIView.animate(withDuration: animated ? 0.3 : 0.0, delay: 0, options: .curveEaseInOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
// TableView
extension PdfVC: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return filtedJsonData.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if filtedJsonData[section].pdfs.count == 0 {
return nil
}
return filtedJsonData[section].section
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filtedJsonData[section].pdfs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PdfCell", for: indexPath) as! PdfCell
let pdf = filtedJsonData[indexPath.section].pdfs[indexPath.row]
cell.setupCell(pdf: pdf)
return cell
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
dismissKeyboard()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let pdf = filtedJsonData[indexPath.section].pdfs[indexPath.row]
//THIS IS WHERE I'M STUCK<<<<<<<<<
self.title = pdf.airport
}
}
// SearchBar
extension PdfVC: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
for (index, data) in jsonData.enumerated() {
// Filter pdfs by code and name
let filtedPdf = data.pdfs.filter { $0.code.lowercased().prefix(searchText.count) == searchText.lowercased() || $0.airport.lowercased().prefix(searchText.count) == searchText.lowercased() }
filtedJsonData[index].pdfs = filtedPdf
}
tableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = ""
tableView.reloadData()
}
}
Got it figured out. Thanks for all who spent some time reading my issue. I changed as follows
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let pdf = filtedJsonData[indexPath.section].pdfs[indexPath.row]
self.fileName = pdf.name
self.title = pdf.airport
DispatchQueue.main.async {
self.reloadWebView()
}
}
and added
private func reloadWebView() {
// Setup WebView
if let fileName = fileName {
isVideo = fileName.contains(".mp4")
if let range = fileName.range(of: isVideo ? ".mp4" : ".pdf") {
self.fileName!.removeSubrange(range)
}
}
if let file = Bundle.main.url(forResource: fileName ?? "", withExtension: isVideo ? ".mp4" : ".pdf", subdirectory: nil, localization: nil) {
let request = URLRequest(url: file)
webView.load(request)
}
}

Passing ImageArray from Json File to ImageView Controller

I'm Trying to have tableview with a search controller pass image Array from Json file to a new viewcontroller based on what option the user picks in search bar tableview. I have created a search bar for my App that displays will display all of the information in the app so the user can easily pick the pictures they want to see. That works normally by using a tableview, and when the user pick a row, it sends a variable with the associated pictures to an image view on the other screen.
Because of the amount of options I have, I created a Json file. I have it coded where it will return options based on what the user types into the search bar. My problem is that I am unable to pass the Image Array from the .json file to the image view controller. It will display the viewcontroller, but the "array" imageview is blank as no picture array is being passed. Below is my code and am wondering if anyone has any ideas that could point me in the right direction, or tell me what I am doing wrong.
Search Bar code:
import UIKit
class ProtocolCell: UITableViewCell {
#IBOutlet weak var pNameLabel: UILabel!
}
extension String {
func trimmed() -> String {
return self.trimmingCharacters(in: .whitespaces)
}
}
class SearchController: UIViewController, UISearchBarDelegate {
/// Search Bar
#IBOutlet weak var pSearchBar: UISearchBar!
/// Proto Array
fileprivate var myProtocols:[Protocols]?
/// Searhed Array
fileprivate var searchedProtocols:[Protocols]?
/// TableView
#IBOutlet weak var protocolsTV: UITableView!
/// Is Searching
fileprivate var isSearching:Bool=false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
protocolsTV.tableFooterView=UIView()
pSearchBar.delegate=self
myProtocols=[Protocols]()
searchedProtocols=[Protocols]()
myProtocols?.removeAll()
searchedProtocols?.removeAll()
myProtocols=Functions.getAllProtocolsFromJson()
if protocolsTV.delegate == nil {
protocolsTV.delegate=self
protocolsTV.dataSource=self
}
protocolsTV.reloadData()
}
}
extension SearchController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return isSearching == false ? (myProtocols?.count ?? 0) : (searchedProtocols?.count ?? 0)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProtocolCell") as! ProtocolCell
cell.pNameLabel.text=isSearching == false ? (myProtocols![indexPath.row].pName ?? "") : (searchedProtocols![indexPath.row].pName ?? "")
return cell
}
//EDIT TABLE FUNCTION HERE!!!!!//
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Is Searching: \(isSearching) ImagesArray: \(isSearching==true ? (searchedProtocols?[indexPath.row].imagesName ?? []) : (myProtocols?[indexPath.row].imagesName ?? []))")
let Vc = self.storyboard?.instantiateViewController(withIdentifier: "imageViewController") as! imageViewController
let home = self.storyboard?.instantiateViewController(withIdentifier: "FIRST") as! ViewController
//switch indexPath.section
// {
// case 0:
if searchedProtocols?[indexPath.row].pName == "test" {
let arrayStorage = myProtocols?[indexPath.row].imagesName ?? []
Vc.passedArray = arrayStorage
print(arrayStorage)
print(myProtocols?[indexPath.row].imagesName ?? [])
self.navigationController?.pushViewController(Vc, animated: true)
}
else {
self.navigationController?.pushViewController(home, animated: true)
}
// break;
// default:
// self.navigationController?.pushViewController(home, animated: true)
// }
}
func updateSearchData(With searchText: String, In searchBar: UISearchBar) {
if searchText.trimmed().count == 0 {
isSearching=false
searchBar.setShowsCancelButton(false, animated: true)
} else {
isSearching=true
searchBar.setShowsCancelButton(true, animated: true)
}
if isSearching {
/// We Are Searching Sort Array
if searchedProtocols == nil { searchedProtocols=[Protocols]() }
searchedProtocols?.removeAll()
searchedProtocols=myProtocols!.filter({($0.pName ?? "").lowercased().contains(searchText.lowercased())})
/// Make Set So, Data isn't Repeated
searchedProtocols=Array(Set(searchedProtocols ?? []))
} else {
/// Searching is Stopped
searchedProtocols?.removeAll()
}
protocolsTV.reloadData()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
updateSearchData(With: searchText, In: searchBar)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.view.endEditing(true)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.setShowsCancelButton(false, animated: true)
searchBar.text=nil
isSearching=false
searchedProtocols?.removeAll()
protocolsTV.reloadData()
self.view.endEditing(true)
}
}
Other Used Code:
class Protocols: NSObject {
var pName:String?
var imagesName:[UIImage]!
override init() {}
init(With Dict: [String:Any]) {
pName=Dict["name"] as? String ?? ""
imagesName=Dict["imagesArray"] as? [UIImage] ?? []
}
ImageViewController:
class imageViewController: UIViewController,GADBannerViewDelegate, UIGestureRecognizerDelegate, UIScrollViewDelegate {
#IBOutlet weak var pageControl: UIPageControl!
var bannerView: GADBannerView!
var index = 0
var mySelectedProtocol:Protocols?
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var myImageView: UIImageView!
#IBAction func pictureSwipe(_ sender: Any) {
let pictureString = self.passedArray[index]
self.myImageView.image = pictureString
index = (index < passedArray.count-1) ? index+1 : 0
self.pageControl.numberOfPages = passedArray.count
self.pageControl.currentPage = index
}
#IBAction func pictureswipeback(_ sender: Any) {
let pictureString = self.passedArray[index]
self.myImageView.image = pictureString
index = (passedArray.count-1)
self.pageControl.numberOfPages = passedArray.count
self.pageControl.currentPage = index
}
func configurePageControl() {
self.pageControl.numberOfPages = passedArray.count
self.pageControl.currentPage = 0
self.pageControl.pageIndicatorTintColor = UIColor.white
self.pageControl.currentPageIndicatorTintColor = UIColor.red
self.view.addSubview(pageControl)
if index == 1 {
self.pageControl.currentPage = 1
}
func updateCurrentPageDisplay(){
self.pageControl.numberOfPages = passedArray.count
}
}
var passedImage : UIImage! = nil
var passedArray : [UIImage]!
override func viewDidLoad(){
super.viewDidLoad()
self.myImageView.image = passedArray.first
configurePageControl()
scrollView.delegate = self
self.navigationController?.navigationBar.isHidden = false
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 5.0
In Destination Controller
class DestinationVC: UIViewController {
var mySelectedProtocol:Protocols?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if mySelectedProtocol == nil { self.navigationController?.popViewController(animated: true) }
/// We have Data
print("Img Array with Name ==> \(mySelectedProtocol?.imagesName ?? [])")
}
}
In Source Controller TableView Delegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Is Searching: \(isSearching) ImagesArray: \(isSearching==true ? (searchedProtocols?[indexPath.row].imagesName ?? []) : (myProtocols?[indexPath.row].imagesName ?? []))")
let vc:DestinationVC=mainStoryBoard.instantiateViewController(withIdentifier: "DestinationVC") as! DestinationVC
vc.mySelectedProtocol=isSearching==true ? (searchedProtocols?[indexPath.row]) : (myProtocols?[indexPath.row])
self.navigationController?.pushViewController(vc, animated: true)
}
Update 2 - Show Images as PageViewController
private func addPageView() {
myScrollView.backgroundColor=UIColor.clear
myScrollView.isUserInteractionEnabled=true
myScrollView.showsHorizontalScrollIndicator=false
myScrollView.isPagingEnabled=true
myScrollView.delegate=self
myScrollView.bounces=false
self.count=mySelectedProtocol!.imagesName!.count
for i in 0..<self.count {
///Get Origin
let xOrigin : CGFloat = CGFloat(i) * myScrollView.frame.size.width
///Create a imageView
let imageView = UIImageView()
imageView.frame = CGRect(x: xOrigin, y: 0, width: myScrollView.frame.size.width, height: myScrollView.frame.size.height)
imageView.contentMode = .scaleAspectFit
imageView.image=UIImage(named: mySelectedProtocol!.imagesName![i])
myScrollView.addSubview(imageView)
}
setUpPageControl()
///Set Content Size to Show
myScrollView.contentSize = CGSize(width: myScrollView.frame.size.width * CGFloat(self.count), height: myScrollView.frame.size.height)
}

how to parse complex JSON and show it inside and outside of CollectionView

I have been dealing with JSON for a month now , getting better day by day. Here is a complex jigsaw I have been circling around. JSON return I get is like this :
{
"main_content": [
{
"product_title": "product 3",
"description": "",
"regular_price": "320",
"sale_price": "",
"product_currency": "$",
"size": [
{
"size_data": "L"
},
{
"size_data": "S"
}
],
"color": [
{
"color_data": "RED"
},
{
"color_data": "WHITE"
}
],
"gallery": [
{
"guid": "http://xxx/wp-content/uploads/2016/11/catagory1.jpg"
},
{
"guid": "http://xxx/wp-content/uploads/2016/11/catagory3.jpg"
}
]
}
]
}
Now , here product_title , description , regular_price , sale_price and product_currency will be outside of array as you can see. For size & color I need to fetch as array outside of my CollectionView , I dunno how to iterate through the array outside of a collectionView or tableView, coz in those I got an indexpath to iterate but I dunno what to do outside a collectionView or tableView. Finally My Image slider will be inside CollectionView therefore gallery items needs to be inside that. Here is visual image of my respective page.
Now here is the POST call using Alamofire in my view
import Foundation
import Alamofire
import SwiftyJSON
//Error could happen for these reason
enum ProductDetailsManagerError: Error {
case network(error: Error)
case apiProvidedError(reason: String)
case authCouldNot(reason: String)
case authLost(reason: String)
case objectSerialization(reason: String)
}
// APIManager Class
class ProductDetailsManager {
// THE RIGHT WAY A.K.A. "THE ONE LINE SINGLETON (NOW WITH PROOF!")
static let sharedInstance = ProductDetailsManager()
func printPublicGists(parameter: [String:Any]? , completionHandler: #escaping (Result<[ProductDetailsJSON]>) -> Void) {
let url: String = "http://xxx/wp-api/products/get_product_informations/"
Alamofire.request(url, method: .post, parameters: parameter, encoding: URLEncoding.default, headers: nil)
.responseJSON { (response) in
guard response.result.error == nil else {
print(response.result.error!)
return
}
guard let value = response.result.value else {
print("no string received in response when swapping oauth code for token")
return
}
print(value)
}
}
func fetchPublicGists(parameter: [String:Any]? , completionHandler: #escaping (Result<[ProductDetailsJSON]>) -> Void) {
let url: String = "http://xxx/wp-api/products/get_product_informations/"
Alamofire.request(url, method: .post, parameters: parameter, encoding: URLEncoding.default, headers: nil)
.responseJSON { response in
let result = self.gistArrayFromResponse(response: response)
completionHandler(result)
}
}
// Download Image from URL
func imageFrom(urlString: String, completionHandler: #escaping (UIImage?, Error?) -> Void) {
let _ = Alamofire.request(urlString)
.response { dataResponse in
// use the generic response serializer that returns Data
guard let data = dataResponse.data else {
completionHandler(nil, dataResponse.error)
return
}
let image = UIImage(data: data)
completionHandler(image, nil)
}
}
//gistArrayFromResponse function
private func gistArrayFromResponse(response: DataResponse<Any>) -> Result<[ProductDetailsJSON]> {
// For Network Error
guard response.result.error == nil else {
print(response.result.error!)
return .failure(RueDu8APIManagerError.network(error: response.result.error!))
}
// JSON Serialization Error, make sure we got JSON and it's an array
guard let jsonArray = response.result.value else {
print("did not get array of homeFeed object as JSON from API")
return .failure(RueDu8APIManagerError.objectSerialization(reason: "Did not get JSON dictionary in response"))
}
//turn JSON into gists
//let gistss = jsonArray.flatMap { HomeFeedJSON(items: $0) }
var gists = [ProductDetailsJSON]()
let jsonR = JSON(jsonArray)
let main_content = jsonR["main_content"].array
for item in main_content! {
gists.append(ProductDetailsJSON(items: item))
}
return .success(gists)
}//gistArrayFromResponse() function ends here
}
here is my model class
import Foundation
import SwiftyJSON
class ProductDetailsJSON {
var _product_title: String?
var _description: String?
var _regular_price: String?
var _sale_price: String?
var _product_currency: String?
var _size: String?
var _color: String?
var _image: URL?
init(items: JSON){
self._product_title = items["product_title"].stringValue
self._description = items["description"].stringValue
self._regular_price = items["regular_price"].stringValue
self._sale_price = items["sale_price"].stringValue
self._product_currency = items["product_currency"].stringValue
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData {
self._size = itemsIMG["size_data"].stringValue
}
let colorData = items["color"].arrayValue
for itemsColor in colorData {
self._size = itemsColor["color_data"].stringValue
}
let galleryImg = items["gallery"].arrayValue
for image in galleryImg {
self._image = image["guid"].URL
}
}
var product_title: String {
if _product_title == nil {
_product_title = ""
}
return _product_title!
}
var description: String {
if _description == nil {
_description = ""
}
return _description!
}
var regular_price: String {
if _regular_price == nil {
_regular_price = ""
}
return _regular_price!
}
var sale_price: String {
if _sale_price == nil {
_sale_price = ""
}
return _sale_price!
}
var product_currency: String {
if _product_currency == nil {
_product_currency = ""
}
return _product_currency!
}
var product_color: String {
if _color == nil {
_color = ""
}
return _size!
}
var product_image: URL {
if _image == nil {
let myURL = "http://www.clker.com/cliparts/d/L/P/X/z/i/no-image-icon-hi.png"
let noImage: URL = URL(string: myURL)!
_image = noImage
}
return _image!
}
}
and here is my controller class where I am struggling to show the size , color and gallery items from JSON
import UIKit
import DropDown
import Alamofire
import SwiftyJSON
class ShopItemVC: UIViewController , UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var contentView: UIView!
#IBOutlet weak var selectedProductImg: UIImageView!
#IBOutlet weak var backgroundCardView1: UIView!
#IBOutlet weak var backgroundCardView2: UIView!
#IBOutlet weak var backgroundCardView3: UIView!
#IBOutlet weak var sizeBtn: NiceButton!
#IBOutlet weak var colorBtn: NiceButton!
#IBOutlet weak var productPrice: UILabel!
#IBOutlet weak var productTitle: UILabel!
// var Title = [ProductDetailsJSON]()
var product_id:Int? //got value from SpecificCatagoryVC
var product_detail = [ProductDetailsJSON]()
var reloadData = 0
let sizeDropDown = DropDown()
let colorDropDown = DropDown()
lazy var dropDowns: [DropDown] = {
return [
self.sizeDropDown,
self.colorDropDown
]
}()
let CatagoryPic = ["catagory1","catagory2","catagory3","catagory4","catagory5","catagory6","c atagory7"]
// let CatagoryPicture = [ProductDetailsJSON]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
sizeBtn.layer.borderWidth = 1.2
sizeBtn.layer.borderColor = UIColor.black.cgColor
colorBtn.layer.borderWidth = 1.2
colorBtn.layer.borderColor = UIColor.black.cgColor
backgroundCardView1.backgroundColor = UIColor.white
backgroundCardView1.layer.cornerRadius = 5.0
backgroundCardView1.layer.masksToBounds = false
backgroundCardView1.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView1.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView1.layer.shadowOpacity = 0.8
backgroundCardView2.backgroundColor = UIColor.white
backgroundCardView2.layer.cornerRadius = 5.0
backgroundCardView2.layer.masksToBounds = false
backgroundCardView2.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView2.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView2.layer.shadowOpacity = 0.8
backgroundCardView3.backgroundColor = UIColor.white
backgroundCardView3.layer.cornerRadius = 5.0
backgroundCardView3.layer.masksToBounds = false
backgroundCardView3.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
backgroundCardView3.layer.shadowOffset = CGSize(width: 0, height: 0)
backgroundCardView3.layer.shadowOpacity = 0.8
setupDropDowns()
}
override func viewDidAppear(_ animated: Bool) {
self.scrollView.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 40)
loadGists(parameter: ["product_id":product_id ?? 0])
}
func setupDropDowns() {
setupSizeDropDown()
setupColorDropDown()
}
func setupSizeDropDown() {
sizeDropDown.anchorView = sizeBtn
sizeDropDown.bottomOffset = CGPoint(x: 0, y: sizeBtn.bounds.height)
// You can also use localizationKeysDataSource instead. Check the docs.
sizeDropDown.dataSource = [
"XXL",
"XL",
"L",
"M",
"S"
]
// Action triggered on selection
sizeDropDown.selectionAction = { [unowned self] (index, item) in
self.sizeBtn.setTitle(item, for: .normal)
print(item)
}
}
func setupColorDropDown() {
colorDropDown.anchorView = colorBtn
colorDropDown.bottomOffset = CGPoint(x: 0, y: colorBtn.bounds.height)
// You can also use localizationKeysDataSource instead. Check the docs.
colorDropDown.dataSource = [
"Red",
"Blue",
"White",
"Purple",
"Pink"
]
// Action triggered on selection
colorDropDown.selectionAction = { [unowned self] (index, item) in
self.colorBtn.setTitle(item, for: .normal)
print(item)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadGists(parameter: [String:Any]?) {
ProductDetailsManager.sharedInstance.fetchPublicGists(parameter: parameter) {
(result) in
guard result.error == nil else {
self.handleLoadGistsError(result.error!)
return
}
if let fetchedGists = result.value {
self.product_detail = fetchedGists
}
self.reloadData = 1
self.collectionView?.reloadData()
}
}
func handleLoadGistsError(_ error: Error) { }
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return CatagoryPic.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ShopItemCell", for: indexPath) as! ShopItemCell
if reloadData == 1 {
let myProduct = self.product_detail[indexPath.row]
self.productTitle.text = myProduct.product_title
}
cell.shopItemPic.image = UIImage(named: CatagoryPic[(indexPath as NSIndexPath).row])
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// here you know which item is selected by accessing indexPath.item property, for example:
let selectedImage = UIImage(named: CatagoryPic[(indexPath as NSIndexPath).item])
selectedProductImg.image = selectedImage
}
#IBAction func sizeBtnPressed(_ sender: Any) {
sizeDropDown.show()
//print("size btn pressed")
}
#IBAction func colorBtnPressed(_ sender: Any) {
colorDropDown.show()
}
#IBAction func backBtn(_ sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
}
There are some other class where I am facing the same issue. Hope If I get this solution I will be able to solve those. Thanks in advance .
First thing to note is that you are returning the size value and not color in the product_color variable.
Also when you loop through the arrays in your JSON, you're setting the variable to only be the final value. For example here:
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData {
self._size = itemsIMG["size_data"].stringValue
}
The JSON is
"size": [
{
"size_data": "L"
},
{
"size_data": "S"
}
]
So _size is going to be set as "S" and "L" will never be assigned. I'd suggest changing _size, _color and _image to
var _sizes: [String] = []
var _colors: [String] = []
var _images: [String] = []
And then when looping through the JSON array:
let sizeData = items["size"].arrayValue
for itemsIMG in sizeData {
let size = itemsIMG["size_data"].stringValue
_sizes.append(size)
}
If I'm understanding correctly, you then want to update your dropdown data when you get the response with the gist.
sizeDropDown.dataSource = product_detail.product_sizes
Then the same for the other drop downs.
And create product_sizes, product_colors and product_images variables in the model similar sale_price and regular_price.

Search with UISearchBar through WebServer

What is the correct procedure to search with UISearchBar through a web server? The following code is what I have tried so far. It is working but not in the right way. I am not getting all the results as it has to be.
class PlacesViewController: UITableViewController, UISearchBarDelegate, UISearchControllerDelegate, UISearchDisplayDelegate {
#IBOutlet var searchController: UISearchBar!
var searchActive: Bool = false
var filtered = [PlacesData]()
var startSearching: Bool = false
var DataTable:[PlacesData] = []
var nameToPass: String!
var totalvisits: String!
var totallikes: String!
override func viewDidLoad() {
tableView.delegate = self
tableView.dataSource = self
searchController.delegate = self
definesPresentationContext = true
searchController.showsCancelButton = true
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
donwloadData()
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchActive = true;
tableView.reloadData()
print("textdidbegin")
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchActive = false;
print("textdidend")
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
searchBar.text = ""
searchBar.resignFirstResponder()
self.filtered.removeAll()
self.tableView.reloadData()
print("textdidcancel")
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searchActive = false;
print("textdidsearch")
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
print("search")
self.filtered.removeAll()
tableView.reloadData()
Alamofire
.request(.POST, "mysite.com/search.php", parameters: ["type":"Stores", "word": searchText])
.responseJSON { response in
if(response.result.description == "SUCCESS"){
let json = JSON(response.result.value!)
let arrayjson = json.arrayValue
let jsondict = arrayjson[0].dictionaryValue
let firstarr = jsondict["words"]?.arrayValue
for item in firstarr!{
let name = item["name"].stringValue
let long = item["long"].stringValue
let lat = item["lat"].stringValue
print(name)
self.filtered.append(PlacesData(name: name, long: long, lat: lat))
}
}
self.tableView.reloadData()
}
}
func donwloadData(){
Alamofire
.request(.GET, "mysite.com/showplaces.php")
.responseJSON { response in
print(response.result)
if(response.result.description == "SUCCESS"){
let json = JSON(response.result.value!)
let arrayjson = json.arrayValue
let jsondict = arrayjson[0].dictionaryValue
let firstarr = jsondict["words"]?.arrayValue
for item in firstarr!{
let name = item["name"].stringValue
let long = item["long"].stringValue
let lat = item["lat"].stringValue
print(name)
self.DataTable.append(PlacesData(name: name, long: long, lat: lat))
}
self.tableView.reloadData()
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (searchActive == true) {
return filtered.count + 1
}else{
return DataTable.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! PlacesCel
let places: PlacesData
if (searchActive == true) {
print("search IS active")
if(indexPath.row == filtered.count){
cell.titleLabel.text = "Add Place"
}else{
places = filtered[indexPath.row]
cell.textLabel?.text = places.name
}
} else {
print("search IS NOT active")
places = DataTable[indexPath.row]
cell.textLabel?.text = places.name
}
return cell
}
I could not find a proper tutorial to find the correct way of doing that. Apparently this is not working properly and whatever I tried it is not working. Any solution or answer would be appreciated. Thank you.
After all the problem was that I was not canceling my requests and every time I was typing a character in the searchbar a new request was added and all were added together at the end after every request was finished. The following code gave me the solution.
if((self.request) != nil){
request?.cancel()
}
self.request = Alamofire()

thread 1 exc_bad_instruction (code=exc_i386_invop, subcode=0x0) using search

I'm trying to use a search function in a table view. the data changes like it supposed to but the moment I hit a cell that has been generated after the search it won't take me to the next view. It crashes on this piece of code on the selectedIndex var.
if self.tableView == self.searchDisplayController!.searchResultsTableView {
var selectedIndex = self.tableView.indexPathForSelectedRow()!.row
selectedTimeSlot = filteredTimeSlots[selectedIndex]
} else {
var selectedIndex = self.tableView.indexPathForSelectedRow()!.row
selectedTimeSlot = timeSlots[selectedIndex]
}
this is my entire code
import UIKit
import Alamofire
class ClockEditTableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate {
// Contains all time slots
var timeSlots: [TimeSlot] = [TimeSlot]()
// Contains the search results
var filteredTimeSlots: [TimeSlot] = [TimeSlot]()
override func viewDidLoad() {
// timeSlots = DataHelper.getTimeSlots()
Alamofire.request(.GET, "http://wouterhabets.com/acts.json")
.response { (request, response, data, error) in
if error == nil {
self.timeSlots = DataHelper.getTimeSlots(data as NSData)
self.tableView.reloadData()
} else {
self.timeSlots = DataHelper.getTimeSlots()
self.tableView.reloadData()
}
}
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var actDetailViewController: ActDetailsViewController = segue.destinationViewController as ActDetailsViewController
var selectedTimeSlot: TimeSlot
if self.tableView == self.searchDisplayController!.searchResultsTableView {
var selectedIndex = self.tableView.indexPathForSelectedRow()!.row
selectedTimeSlot = filteredTimeSlots[selectedIndex]
} else {
var selectedIndex = self.tableView.indexPathForSelectedRow()!.row
selectedTimeSlot = timeSlots[selectedIndex]
}
actDetailViewController.timeSlot = selectedTimeSlot
}
// Return the amount of cells to display
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int
// Check if the user is searching
if tableView == self.searchDisplayController!.searchResultsTableView {
count = filteredTimeSlots.count
} else {
count = timeSlots.count
}
return count
}
// Fill cells with data
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("actCell", forIndexPath: indexPath) as UITableViewCell
var timeSlot: TimeSlot
// Check if the user is searching
if tableView == self.searchDisplayController!.searchResultsTableView {
timeSlot = filteredTimeSlots[indexPath.row]
} else {
timeSlot = timeSlots[indexPath.row]
}
cell.textLabel?.text = timeSlot.act?.title
return cell
}
func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
self.filterContentForSearchText(searchString)
return true
}
func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchScope searchOption: Int) -> Bool {
self.filterContentForSearchText(self.searchDisplayController!.searchBar.text)
return true
}
func filterContentForSearchText(searchText: String) {
self.filteredTimeSlots = self.timeSlots.filter({( timeSlot: TimeSlot) -> Bool in
// Optional scope search code
// let categoryMatch = (scope == "All") || (band.category == scope)
let searchText = searchText.lowercaseString
let stringMatch = timeSlot.act?.title?.lowercaseString.rangeOfString(searchText)
return (stringMatch != nil) // && categoryMatch
})
}
}
Does anyone have a clue what I'm doing wrong and what I should do to fix this
self.tableView is the main table view, so
self.tableView == self.searchDisplayController!.searchResultsTableView
will never be true
You should check if the active tableView is the main tableView or the search tableView
if self.searchDisplayController.active {
// get the index path from search table ...
} else {
// get the index path from main table ...
}
That was answered here too.
searchDisplayController, UITableView, Core Data and Swift
Also, be careful when using the exlamation in
self.tableView.indexPathForSelectedRow()!
since that could return nil, and if it returns nil you will get a runtime error