accessibilityIncrement / Decrement not called - ios

I looked and cannot find an answer that works for me. I have subclassed UIControl to create a double-knob slider control. I want each knob to be available for voiceover.
To do this, I create UIAccessibilityElements and add them to an array:
func addAccessibilityElements() {
axKnobs = []
let lowKnob = UIAccessibilityElement(accessibilityContainer: self)
lowKnob.accessibilityLabel = doubleKnob ? lowValueKnobAccessibilityLabel : valueKnobAccessibilityLabel
lowKnob.accessibilityPath = UIAccessibilityConvertPathToScreenCoordinates(knobBezierPath(lowKnobPoint), self)
lowKnob.accessibilityTraits = UIAccessibilityTraitAdjustable
lowKnob.accessibilityValue = "\(lowValue)"
axKnobs.append(lowKnob)
if doubleKnob, let highKnobPoint = highKnobPoint {
let highKnob = UIAccessibilityElement(accessibilityContainer: self)
highKnob.accessibilityLabel = highValueKnobAccessibilityLabel
highKnob.accessibilityPath = UIAccessibilityConvertPathToScreenCoordinates(knobBezierPath(highKnobPoint), self)
highKnob.accessibilityTraits = UIAccessibilityTraitAdjustable
highKnob.accessibilityValue = "\(highValue)"
axKnobs.append(highKnob)
}
}
This seems to work perfect. These methods are called and the interface seems to work right:
override func accessibilityElementCount() -> Int {
return axKnobs.count
}
override func indexOfAccessibilityElement(element: AnyObject) -> Int {
let index = axKnobs.indexOf(element as! UIAccessibilityElement)!
if index == 0 {
currentKnob = .Low
} else {
currentKnob = .High
}
return index
}
override func accessibilityElementAtIndex(index: Int) -> AnyObject? {
return axKnobs[index]
}
However, my last 2 methods (accessibilityIncrement and accessibilityDecrement) in the class extension aren't being called at all.
override func accessibilityIncrement() {
if currentKnob == .None {
return
}
if currentKnob == .High {
highValue = max(highValue + 10, maximumValue)
} else {
if doubleKnob {
lowValue = max(lowValue + 10, highValue - 1)
} else {
lowValue = max(lowValue + 10, maximumValue)
}
}
updateDelegate()
redraw()
}
override func accessibilityDecrement() {
if currentKnob == .None {
return
}
if currentKnob == .High {
highValue = min(highValue - 10, lowValue + 1)
} else {
lowValue = min(lowValue - 10, minimumValue)
}
updateDelegate()
redraw()
}
Any ideas why? Full project available at https://github.com/AaronBratcher/SliderTest

UIAccessibilityElements have those 2 methods called, not the UIControl subclass.
extension DLSlider {
class KnobAccessibilityElement: UIAccessibilityElement {
var onIncrement: ((knob: UIAccessibilityElement) -> Void)?
var onDecrement: ((knob: UIAccessibilityElement) -> Void)?
override func accessibilityIncrement() {
if let callback = onIncrement {
callback(knob: self)
}
}
override func accessibilityDecrement() {
if let callback = onDecrement {
callback(knob: self)
}
}
}
func addAccessibilityElements() {
axKnobs = []
let lowKnob = KnobAccessibilityElement(accessibilityContainer: self)
lowKnob.accessibilityLabel = doubleKnob ? lowValueKnobAccessibilityLabel : valueKnobAccessibilityLabel
lowKnob.accessibilityPath = UIAccessibilityConvertPathToScreenCoordinates(knobBezierPath(lowKnobPoint), self)
lowKnob.accessibilityTraits = UIAccessibilityTraitAdjustable
lowKnob.accessibilityValue = "\(lowValue)"
lowKnob.onIncrement = { [unowned self] (knob: UIAccessibilityElement) in
self.incrementKnob(knob)
}
lowKnob.onDecrement = { [unowned self] (knob: UIAccessibilityElement) in
self.decrementKnob(knob)
}
axKnobs.append(lowKnob)
if doubleKnob, let highKnobPoint = highKnobPoint {
let highKnob = KnobAccessibilityElement(accessibilityContainer: self)
highKnob.accessibilityLabel = highValueKnobAccessibilityLabel
highKnob.accessibilityPath = UIAccessibilityConvertPathToScreenCoordinates(knobBezierPath(highKnobPoint), self)
highKnob.accessibilityTraits = UIAccessibilityTraitAdjustable
highKnob.accessibilityValue = "\(highValue)"
highKnob.onIncrement = { [unowned self] (knob: UIAccessibilityElement)in
self.incrementKnob(knob)
}
highKnob.onDecrement = { [unowned self] (knob: UIAccessibilityElement) in
self.decrementKnob(knob)
}
axKnobs.append(highKnob)
}
}
override func accessibilityElementCount() -> Int {
return axKnobs.count
}
override func indexOfAccessibilityElement(element: AnyObject) -> Int {
return axKnobs.indexOf(element as! UIAccessibilityElement)!
}
override func accessibilityElementAtIndex(index: Int) -> AnyObject? {
return axKnobs[index]
}
... // other methods here
}

Related

Image change on button is not reflecting in iOS swift by using for loop

I have a situation where 9 buttons are placed on viewController and making tic-tac-toe function on ios. whenever I am trying to reset button images once the game is finished, button images are not getting reset.
class TicTacToeViewController: UIViewController {
#IBOutlet weak var gameBoardView: UIView!
#IBOutlet var buttons: [UIButton]!
var viewModel = TicTacToeViewModel()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
func configureUI() {
viewModel.delegate = self
}
#IBAction func buttonClicked(_ sender: UIButton) {
viewModel.processPlayerMove(for: sender.tag)
}
/// This **function is to reset the view means remove close and circle image once player wants to restart the game. But this code is not working**
func updateButtonView() {
DispatchQueue.main.async {
for (index, moves) in self.viewModel.moves.enumerated() {
let button = self.buttons[index]
if let moves = moves {
let image = UIImage(named: moves.indicator)
button.setImage(image, for: .normal)
} else {
button.setImage(nil, for: .normal)
}
}
}
}
}
/// Delegates are called from viewmodel
extension TicTacToeViewController: TicTacToeViewModelProtocol {
func updatePlayerInfo(index: Int) {
let systemImageName = viewModel.moves[index - 1]?.indicator ?? ""
let image = UIImage(named: systemImageName)
if let arrayButtons = buttons {
let button = arrayButtons[index - 1]
button.setImage(image, for: .normal)
}
}
func displayAlert(alertData: AlertItem) {
let alert = UIAlertController(title: alertData.title, message: alertData.message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: alertData.buttonTitle, style: UIAlertAction.Style.default, handler: { [weak self] _ in
self?.viewModel.resetGame()
self?.updateButtonView()
}))
self.present(alert, animated: true, completion: nil)
}
func gameBoardDisabled(isGameBoardDisable: Bool) {
gameBoardView.isUserInteractionEnabled = !isGameBoardDisable
}
}
Please let me know of any confusion. Thanks in advance for your help.
This is view model class
import Foundation
protocol TicTacToeViewModelProtocol: AnyObject {
func updatePlayerInfo(index: Int)
func displayAlert(alertData: AlertItem)
func gameBoardDisabled(isGameBoardDisable: Bool)
}
enum Player {
case human
case computer
}
class TicTacToeViewModel {
var currentMovePosition: Int?
var moves: [Move?] = Array(repeating: nil, count: 9) {
didSet {
if let delegate = delegate, let position = self.currentMovePosition {
delegate.updatePlayerInfo(index: position)
}
}
}
var isGameBoardDisable = false {
didSet {
if let delegate = delegate {
delegate.gameBoardDisabled(isGameBoardDisable: isGameBoardDisable)
}
}
}
var alertItem: AlertItem? {
didSet {
if let delegate = delegate, let alertItem = alertItem {
delegate.displayAlert(alertData: alertItem)
}
}
}
let winPatterns: Set<Set<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9], [1, 5, 9], [3, 5, 7]]
weak var delegate: TicTacToeViewModelProtocol!
func processPlayerMove(for position: Int) {
self.currentMovePosition = position
if isSquareOccupied(in: moves, forIndex: position) { return }
moves[position - 1] = Move(player: .human, boardIndex: position)
// logic for win, draw or lose
if checkWinCondition(for: .human, in: moves) {
alertItem = AlertContext.humanWin
return
}
if checkForDraw(in: moves) {
alertItem = AlertContext.draw
return
}
isGameBoardDisable = true
processComputerMove()
}
func processComputerMove() {
let computerPosition = determinComputerMovePosition(in: moves)
self.currentMovePosition = computerPosition
moves[computerPosition - 1] = Move(player: .computer, boardIndex: computerPosition)
isGameBoardDisable = false
if checkWinCondition(for: .computer, in: moves) {
alertItem = AlertContext.computerWin
return
}
if checkForDraw(in: moves) {
alertItem = AlertContext.draw
return
}
}
func isSquareOccupied(in moves:[Move?], forIndex index: Int) -> Bool {
return moves.contains(where: { $0?.boardIndex == index })
}
func determinComputerMovePosition(in moves: [Move?]) -> Int {
let computerMoves = moves.compactMap { $0 }.filter { $0.player == .computer }
let computerPositions = Set(computerMoves.map { $0.boardIndex })
for pattern in winPatterns {
let winPositions = pattern.subtracting(computerPositions)
if winPositions.count == 1 {
let isAvailable = !isSquareOccupied(in: moves, forIndex: winPositions.first!)
if isAvailable { return winPositions.first! }
}
}
// if the AI ​​can't finish the game it will block
let humanMoves = moves.compactMap { $0 }.filter { $0.player == .human }
let humanPositions = Set(humanMoves.map { $0.boardIndex })
for pattern in winPatterns {
let winPositions = pattern.subtracting(humanPositions)
if winPositions.count == 1 {
let isAvailable = !isSquareOccupied(in: moves, forIndex: winPositions.first!)
if isAvailable { return winPositions.first! }
}
}
// always take the middle block
let middleSquare = 5
if !isSquareOccupied(in: moves, forIndex: middleSquare) {
return middleSquare
}
// if the AI ​​can't get position middle block it will get a random position
var movePosition = Int.random(in: 1..<10)
while isSquareOccupied(in: moves, forIndex: movePosition) {
movePosition = Int.random(in: 1..<10)
}
return movePosition
}
func checkWinCondition(for player: Player, in moves:[Move?]) -> Bool {
let playerMoves = moves.compactMap({ $0 }).filter { $0.player == player }
let playerPositions = Set(playerMoves.map { $0.boardIndex })
for pattern in winPatterns where pattern.isSubset(of: playerPositions) {return true}
return false
}
func checkForDraw(in moves: [Move?]) -> Bool {
return moves.compactMap { $0 }.count == 9
}
func resetGame() {
moves = Array(repeating: nil, count: 9)
}
}

NSPopoverTouchBarItems in NSScrollView (NSTouchBar)

Is there a way to add an array of NSPopoverTouchBarItems into a NSScrollView?
Currently, my view hierarchy resembles the below list.
NSTouchBar
NSCustomTouchBarItem
NSScrollView
NSStackView
Array of NSButtons
The above hierarchy outputs the following screenshot.
In sum, the end goal is to replace the array of NSButtons with NSPopoverTouchBarItems.
I believe what you need is the use of NSScrubber to be able to scroll or have fixed position of multiple buttons including NSPopoverTouchBarItem
https://developer.apple.com/documentation/appkit/nsscrubber
Check out this repository for more information and sample codes that might help you:
https://github.com/loretoparisi/touchbar
import Cocoa
fileprivate extension NSTouchBar.CustomizationIdentifier {
static let popoverBar = NSTouchBar.CustomizationIdentifier("com.TouchBarCatalog.popoverBar")
}
fileprivate extension NSTouchBarItem.Identifier {
static let scrubberPopover = NSTouchBarItem.Identifier("com.TouchBarCatalog.TouchBarItem.scrubberPopover")
}
class PopoverScrubber: NSScrubber {
var presentingItem: NSPopoverTouchBarItem?
}
class PopoverScrubberViewController: NSViewController {
// MARK: NSTouchBar
override func makeTouchBar() -> NSTouchBar? {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.customizationIdentifier = .popoverBar
touchBar.defaultItemIdentifiers = [.scrubberPopover]
touchBar.customizationAllowedItemIdentifiers = [.scrubberPopover]
return touchBar
}
}
// MARK: NSTouchBarDelegate
extension PopoverScrubberViewController: NSTouchBarDelegate {
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
guard identifier == NSTouchBarItem.Identifier.scrubberPopover else { return nil }
let popoverItem = NSPopoverTouchBarItem(identifier: identifier)
popoverItem.collapsedRepresentationLabel = "Scrubber Popover"
popoverItem.customizationLabel = "Scrubber Popover"
let scrubber = PopoverScrubber()
scrubber.register(NSScrubberTextItemView.self, forItemIdentifier: NSUserInterfaceItemIdentifier(rawValue: "TextScrubberItemIdentifier"))
scrubber.mode = .free
scrubber.selectionBackgroundStyle = .roundedBackground
scrubber.delegate = self
scrubber.dataSource = self
scrubber.presentingItem = popoverItem
popoverItem.collapsedRepresentation = scrubber
popoverItem.popoverTouchBar = PopoverTouchBarSample(presentingItem: popoverItem)
return popoverItem
}
}
// MARK: NSScrubber Data Source and delegate
extension PopoverScrubberViewController: NSScrubberDataSource, NSScrubberDelegate {
func numberOfItems(for scrubber: NSScrubber) -> Int {
return 20
}
func scrubber(_ scrubber: NSScrubber, viewForItemAt index: Int) -> NSScrubberItemView {
let itemView = scrubber.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "TextScrubberItemIdentifier"), owner: nil) as! NSScrubberTextItemView
itemView.textField.stringValue = String(index)
return itemView
}
func scrubber(_ scrubber: NSScrubber, didSelectItemAt index: Int) {
print("\(#function) at index \(index)")
if let popoverScrubber = scrubber as? PopoverScrubber,
let popoverItem = popoverScrubber.presentingItem {
popoverItem.showPopover(nil)
}
}
}

CollectionView updates and duplicated sequentially in my chat view. (Swift 4.2, Xcode)(MessengerKit)

I have been using 'MessengerKit' for my chat section of my app. I can write and ready messages from firebase but when the chat updates, the messages duplicates sequentially(once, twice, thrice and so on). I am attaching my chatViewController Code below along with the message view on the app.
chatViewController Code :
class chatViewController: MSGMessengerViewController {
// Users in the chat
var nameOfHirer : String = ""
var nameofSeeker : String = ""
var seekerData = User(displayName: "", avatar: nil, isSender: false)
var hirerData = User(displayName: "", avatar: nil, isSender: true)
var id = 100
// Messages
lazy var messages: [[MSGMessage]] = []
func retrieveSeeker() {
let db = Firestore.firestore()
db.collection("Posts").document(jobID).collection("Applications").whereField("ID", isEqualTo: userID).getDocuments { (document, error) in
for document in document!.documents {
if error != nil {
}else {
let Name = document.get("Name") as! String
self.nameofSeeker = Name
let seeker = User(displayName: Name, avatar: nil, isSender: false)
self.seekerData = seeker
}
}
}
}
func retrieveHirer() {
let db = Firestore.firestore()
db.collection("Posts").document(jobID).getDocument { (document, error) in
if error != nil {
}else {
let Hirer = document?.get("Company Name") as! String
self.nameOfHirer = Hirer
let hirer = User(displayName: Hirer, avatar: nil, isSender: true)
self.hirerData = hirer
}
}
}
var uniqueID : String = ""
var messageBody : String = ""
var jobID : String = ""
var userID : String = ""
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
retrieveHirer()
retrieveSeeker()
// retrieveMessages()
print(messageBody)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = true
}
override var style: MSGMessengerStyle {
var style = MessengerKit.Styles.travamigos
style.inputPlaceholder = "Type your message here"
return style
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
collectionView.scrollToBottom(animated: false)
}
override func inputViewPrimaryActionTriggered(inputView: MSGInputView) {
id += 1
var newMessage : String = ""
let messageDictionary = ["Sender": userID, "MessageBody": inputView.message, "JobID": jobID]
// let body = MSGMessageBody.text(newMessage)
//
// let message = MSGMessage(id: id, body: body, user: hirerData, sentAt: Date())
inputView.resignFirstResponder()
let messageDB = Database.database().reference().child("Messages").child(jobID).child(userID)
messageDB.childByAutoId().setValue(messageDictionary) { (error, reference) in
if error != nil {
}else {
retrievemess()
}
}
func retrievemess() {
let messageDB = Database.database().reference().child("Messages").child(jobID).child(userID).queryLimited(toLast: 1)
messageDB.observe(.childAdded) { (snapshot) in
let value = snapshot.value as? [String: AnyObject]
let allmessage = value!["MessageBody"]
let body = MSGMessageBody.text(allmessage as! String)
let newmessage = MSGMessage(id: self.id, body: body, user: self.hirerData, sentAt: Date())
self.insert(newmessage)
}
}
}
override func insert(_ message: MSGMessage) {
collectionView.performBatchUpdates({
if let lastSection = self.messages.last, let lastMessage = lastSection.last, lastMessage.user.displayName == message.user.displayName {
self.messages[self.messages.count - 1].append(message)
let sectionIndex = self.messages.count - 1
let itemIndex = self.messages[sectionIndex].count - 1
self.collectionView.insertItems(at: [IndexPath(item: itemIndex, section: sectionIndex)])
} else {
print(messages.count)
self.messages.append([message])
let sectionIndex = self.messages.count - 1
self.collectionView.insertSections([sectionIndex])
}
}, completion: { (_) in
self.collectionView.scrollToBottom(animated: true)
self.collectionView.layoutTypingLabelIfNeeded()
})
}
override func insert(_ messages: [MSGMessage], callback: (() -> Void)? = nil) {
collectionView.performBatchUpdates({
for message in messages {
if let lastSection = self.messages.last, let lastMessage = lastSection.last, lastMessage.user.displayName == message.user.displayName {
self.messages[self.messages.count - 1].append(message)
let sectionIndex = self.messages.count - 1
let itemIndex = self.messages[sectionIndex].count - 1
self.collectionView.insertItems(at: [IndexPath(item: itemIndex, section: sectionIndex)])
} else {
self.messages.append([message])
let sectionIndex = self.messages.count - 1
self.collectionView.insertSections([sectionIndex])
}
}
}, completion: { (_) in
self.collectionView.scrollToBottom(animated: false)
self.collectionView.layoutTypingLabelIfNeeded()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
callback?()
}
})
}
}
// MARK: - MSGDataSource
extension chatViewController: MSGDataSource {
func numberOfSections() -> Int {
return messages.count
}
func numberOfMessages(in section: Int) -> Int {
return messages[section].count
}
func message(for indexPath: IndexPath) -> MSGMessage {
return messages[indexPath.section][indexPath.item]
}
func footerTitle(for section: Int) -> String? {
return "Just now"
}
func headerTitle(for section: Int) -> String? {
return messages[section].first?.user.displayName
}
}
// MARK: - MSGDelegate
extension chatViewController: MSGDelegate {
func linkTapped(url: URL) {
print("Link tapped:", url)
}
func avatarTapped(for user: MSGUser) {
print("Avatar tapped:", user)
}
func tapReceived(for message: MSGMessage) {
print("Tapped: ", message)
}
func longPressReceieved(for message: MSGMessage) {
print("Long press:", message)
}
func shouldDisplaySafari(for url: URL) -> Bool {
return true
}
func shouldOpen(url: URL) -> Bool {
return true
}
}
The pod I'm using is - https://github.com/steve228uk/MessengerKit
Screenshot:
Ok So I got the issue. The problem is that with
.queryLimited(toLast: 1).observe(.childAdded)
gives back multiple entries after first the first time its fired. The solution was to change .observe to .observeSingleEvent

Picker View of FUIValuePickerFormCell wont appear

I've autogenerated an application from the SAP Cloud SDK Assistant, and I'd like to use a FUIValuePickerFormCell in the detail view of the application, but can't seem to do so.
I've used the sample codes given by the SAP Fiori mentor app, and added a Table View Cell of class "FUIValuePickerFormCell" in main.storyboard, but nothing happens when the cell is tapped, the pickerview doesn't come up, nor is the cell editable. Does anyone knows why is this so? Below are the codes that I used
Cell that i want to change in ProductsTypeDetailTableDelegate
case 6:
let cell = tableView.dequeueReusableCell(withIdentifier: FUIValuePickerFormCell.reuseIdentifier, for: indexPath) as! FUIValuePickerFormCell
valuePickerCell = cell
cell.isEditable = true
cell.keyName = "Appointment Status"
cell.valueOptions = ["1", "2", "3"]
cell.value = 1 //index of first value
cell.onChangeHandler = { newValue in
if let option = self.valuePickerCell?.valueOptions[newValue]{
print("Selected value option \(option)")
}
}
return cell
DetailViewController:
import SAPFoundation
import SAPOData
import SAPFiori
import SAPCommon
class DetailViewController: FUIFormTableViewController, Notifier, LoadingIndicator {
private let appDelegate = UIApplication.shared.delegate as! AppDelegate
private var tableDelegate: DetailTableDelegate!
var tableUpdater: TableUpdaterDelegate?
var loadingIndicator: FUILoadingIndicatorView?
private let logger = Logger.shared(named: "DetailViewControllerLogger")
var services: ServicesDataAccess {
return appDelegate.services
}
// The Entity which will be edited on the Detail View
var selectedEntity: EntityValue!
var entityArray: [EntityValue]!
var entityArray2: [EntityValue]!
var entityArray3: [EntityValue]!
var prodimages: [EntityValue]!
var collectionType: CollectionType = .none {
didSet {
if let delegate = self.generatedTableDelegate() {
self.tableDelegate = delegate
if self.selectedEntity != nil {
self.tableDelegate.entity = self.selectedEntity
}
if self.entityArray != nil {
self.tableDelegate.arrayEntity = self.entityArray
}
if self.entityArray2 != nil {
self.tableDelegate.arrayEntity2 = self.entityArray2
}
if self.entityArray3 != nil {
self.tableDelegate.arrayEntity3 = self.entityArray3
}
if self.prodimages != nil{
self.tableDelegate.prodimages = self.prodimages
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.allowsSelection = false
self.tableView.dataSource = tableDelegate
self.tableView.delegate = tableDelegate
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 44
}
#IBAction func updateEntity(_ sender: AnyObject) {
self.showIndicator()
self.view.endEditing(true)
self.logger.info("Updating entity in backend.")
self.services.service.updateEntity(self.tableDelegate.entity) { error in
self.hideIndicator()
if let error = error {
self.logger.error("Update entry failed.", error: error)
self.displayAlert(title: NSLocalizedString("keyErrorEntityUpdateTitle", value: "Update entry failed", comment: "XTIT: Title of alert message about entity update failure."),
message: NSLocalizedString("keyErrorEntityUpdateBody", value: error.localizedDescription, comment: "XMSG: Body of alert message about entity update failure."))
return
}
self.logger.info("Update entry finished successfully.")
FUIToastMessage.show(message: NSLocalizedString("keyUpdateEntityFinishedTitle", value: "Updated", comment: "XTIT: Title of alert message about successful entity update."))
self.tableUpdater?.updateTable()
}
}
func createEntity() {
self.showIndicator()
self.view.endEditing(true)
self.logger.info("Creating entity in backend.")
self.services.service.createEntity(self.tableDelegate.entity) { error in
self.hideIndicator()
if let error = error {
self.logger.error("Create entry failed.", error: error)
self.displayAlert(title: NSLocalizedString("keyErrorEntityCreationTitle", value: "Create entry failed", comment: "XTIT: Title of alert message about entity creation error."),
message: NSLocalizedString("keyErrorEntityCreationBody", value: error.localizedDescription, comment: "XMSG: Body of alert message about entity creation error."))
return
}
self.logger.info("Create entry finished successfully.")
DispatchQueue.main.async {
self.dismiss(animated: true) {
FUIToastMessage.show(message: NSLocalizedString("keyEntityCreationBody", value: "Created", comment: "XMSG: Title of alert message about successful entity creation."))
self.tableUpdater?.updateTable()
}
}
}
}
func cancel() -> Void {
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
}
}
// Test code
private func updateTable(completionHandler: #escaping() -> Void) {
self.tableDelegate?.requestEntities { error in
defer {
completionHandler()
}
if let error = error {
self.displayAlert(title: NSLocalizedString("keyErrorLoadingData", value: "Loading data failed!", comment: "XTIT: Title of loading data error pop up."),
message: error.localizedDescription)
self.logger.error("Could not update table.", error: error)
return
}
DispatchQueue.main.async {
self.tableView.reloadData()
self.logger.info("Table updated successfully!")
}
}
}
private func configureView() {
if self.collectionType != .none {
self.title = collectionType.rawValue
if let tableDelegate = self.generatedTableDelegate() {
self.tableDelegate = tableDelegate
if let tableView = self.tableView {
tableView.delegate = tableDelegate
tableView.dataSource = tableDelegate
self.updateTable()
}
}
}
}
func updateTable() {
self.showIndicator()
DispatchQueue.global().async {
self.updateTable() {
self.hideIndicator()
}
}
}
// test code ends
}
DetailTableDelegate:
import SAPOData
import SAPFiori
import SAPFoundation
protocol DetailTableDelegate: UITableViewDelegate, UITableViewDataSource {
var entity: EntityValue { get set }
var arrayEntity: [EntityValue] { get set }
var arrayEntity2: [EntityValue] { get set }
var arrayEntity3: [EntityValue] { get set }
var prodimages: [EntityValue] { get set}
}
extension DetailTableDelegate {
}
extension DetailViewController {
func generatedTableDelegate() -> DetailTableDelegate? {
switch self.collectionType {
case .customers:
return CUSTOMERSTypeDetailTableDelegate(dataAccess: self.services, rightBarButton: self.navigationItem.rightBarButtonItem!)
case .suppliers:
return SUPPLIERSTypeDetailTableDelegate(dataAccess: self.services, rightBarButton: self.navigationItem.rightBarButtonItem!)
case .orders:
return ORDERSTypeDetailTableDelegate(dataAccess: self.services, rightBarButton: self.navigationItem.rightBarButtonItem!, count: Int())
case .products:
return PRODUCTSTypeDetailTableDelegate(dataAccess: self.services, rightBarButton: self.navigationItem.rightBarButtonItem!)
default:
return nil
}
}
}
ProductsTypeDetailTableDelegate:
import Foundation
import UIKit
import SAPOData
import SAPCommon
import SAPFiori
class PRODUCTSTypeDetailTableDelegate: NSObject, DetailTableDelegate {
private let dataAccess: ServicesDataAccess
private var _entity: PRODUCTSType?
// test code
private var _arrayEntity: [PRODUCTSType] = [PRODUCTSType]()
private var _arrayEntity2: [PRODUCTSType] = [PRODUCTSType]()
private var _arrayEntity3: [PRODUCTSType] = [PRODUCTSType]()
private var _prodimages: [PRODIMGType] = [PRODIMGType]()
var valuePickerCell: FUIValuePickerFormCell?
var prodimages: [EntityValue] {
get {
return _prodimages
}
set {
self._prodimages = newValue as! [PRODIMGType]
}
}
var arrayEntity: [EntityValue] {
get {
return _arrayEntity
}
set {
self._arrayEntity = newValue as! [PRODUCTSType]
}
}
var arrayEntity2: [EntityValue] {
get {
return _arrayEntity2
}
set {
self._arrayEntity2 = newValue as! [PRODUCTSType]
}
}
var arrayEntity3: [EntityValue] {
get {
return _arrayEntity3
}
set {
self._arrayEntity3 = newValue as! [PRODUCTSType]
}
}
// test code ends
var entity: EntityValue {
get {
if _entity == nil {
_entity = createEntityWithDefaultValues()
}
return _entity!
}
set {
_entity = newValue as? PRODUCTSType
}
}
var rightBarButton: UIBarButtonItem
private var validity = Array(repeating: true, count: 8)
init(dataAccess: ServicesDataAccess, rightBarButton: UIBarButtonItem) {
self.dataAccess = dataAccess
self.rightBarButton = rightBarButton
self.rightBarButton.isEnabled = false
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let currentEntity = self.entity as? PRODUCTSType else {
return cellForDefault(tableView: tableView, indexPath: indexPath)
}
switch indexPath.row {
case 0:
var value = ""
let name = "Product ID"
if currentEntity.hasDataValue(for: PRODUCTSType.prodid) {
value = "\(currentEntity.prodid)"
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.prodid, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
if let validValue = TypeValidator.validInteger(from: newValue) {
currentEntity.prodid = validValue
self.validity[0] = true
} else {
self.validity[0] = false
}
self.barButtonShouldBeEnabled()
return self.validity[0]
})
case 1:
var value = ""
let name = "Name"
if currentEntity.hasDataValue(for: PRODUCTSType.prodname) {
if let prodname = currentEntity.prodname {
value = "\(prodname)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.prodname, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.prodname = nil
self.validity[1] = false
} else {
if let validValue = TypeValidator.validString(from: newValue, for: PRODUCTSType.prodname) {
currentEntity.prodname = validValue
self.validity[1] = true
} else {
self.validity[1] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[1]
})
case 2:
var value = ""
let name = "Description"
if currentEntity.hasDataValue(for: PRODUCTSType.proddesc) {
if let proddesc = currentEntity.proddesc {
value = "\(proddesc)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.proddesc, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.proddesc = nil
self.validity[2] = false
} else {
if let validValue = TypeValidator.validString(from: newValue, for: PRODUCTSType.proddesc) {
currentEntity.proddesc = validValue
self.validity[2] = true
} else {
self.validity[2] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[2]
})
case 3:
var value = ""
let name = "Current Stock"
if currentEntity.hasDataValue(for: PRODUCTSType.currstock) {
if let currstock = currentEntity.currstock {
value = "\(currstock)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.currstock, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.currstock = 0
self.validity[3] = true
} else {
if let validValue = TypeValidator.validInteger(from: newValue) {
currentEntity.currstock = validValue
self.validity[3] = true
} else {
self.validity[3] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[3]
})
case 4:
var value = ""
let name = "Minimum Stock"
if currentEntity.hasDataValue(for: PRODUCTSType.minstock) {
if let minstock = currentEntity.minstock {
value = "\(minstock)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.minstock, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.minstock = 0
self.validity[4] = true
} else {
if let validValue = TypeValidator.validInteger(from: newValue) {
currentEntity.minstock = validValue
self.validity[4] = true
} else {
self.validity[4] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[4]
})
case 5:
var value = ""
let name = "Price"
if currentEntity.hasDataValue(for: PRODUCTSType.price) {
if let price = currentEntity.price {
value = "\(price)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.price, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.price = nil
self.validity[5] = false
} else {
if let validValue = TypeValidator.validBigDecimal(from: newValue) {
currentEntity.price = validValue
self.validity[5] = true
} else {
self.validity[5] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[5]
})
case 6:
// var value = ""
// let name = "Category"
// if currentEntity.hasDataValue(for: PRODUCTSType.cat) {
// if let cat = currentEntity.cat {
// value = "\(cat)"
// }
// }
// return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.cat, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// // The property is optional, so nil value can be accepted
// if newValue.isEmpty {
// currentEntity.cat = nil
// self.validity[6] = false
// } else {
// if let validValue = TypeValidator.validString(from: newValue, for: PRODUCTSType.cat) {
// currentEntity.cat = validValue
// self.validity[6] = true
// } else {
// self.validity[6] = false
// }
// }
// self.barButtonShouldBeEnabled()
// return self.validity[6]
// })
let cell = tableView.dequeueReusableCell(withIdentifier: FUIValuePickerFormCell.reuseIdentifier, for: indexPath) as! FUIValuePickerFormCell
valuePickerCell = cell
cell.isEditable = true
cell.keyName = "Appointment Status"
cell.valueOptions = ["1", "2", "3"]
cell.value = 1 //index of first value
cell.onChangeHandler = { newValue in
if let option = self.valuePickerCell?.valueOptions[newValue]{
print("Selected value option \(option)")
}
}
return cell
case 7:
var value = ""
let name = "Supplier ID"
if currentEntity.hasDataValue(for: PRODUCTSType.suppid) {
if let suppid = currentEntity.suppid {
value = "\(suppid)"
}
}
return cellForProperty(tableView: tableView, indexPath: indexPath, property: PRODUCTSType.suppid, value: value, name: name, changeHandler: { (newValue: String) -> Bool in
// The property is optional, so nil value can be accepted
if newValue.isEmpty {
currentEntity.suppid = nil
self.validity[7] = true
} else {
if let validValue = TypeValidator.validInteger(from: newValue) {
currentEntity.suppid = validValue
self.validity[7] = true
} else {
self.validity[7] = false
}
}
self.barButtonShouldBeEnabled()
return self.validity[7]
})
default:
return cellForDefault(tableView: tableView, indexPath: indexPath)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func createEntityWithDefaultValues() -> PRODUCTSType {
let newEntity = PRODUCTSType()
newEntity.prodid = self._arrayEntity.count + 1
return newEntity
}
// Check if all text fields are valid
private func barButtonShouldBeEnabled() {
let anyFieldInvalid = self.validity.first { (field) -> Bool in
return field == false
}
self.rightBarButton.isEnabled = anyFieldInvalid == nil
}
func cellForProperty(tableView: UITableView, indexPath: IndexPath, property: Property, value: String, name: String, changeHandler: #escaping((String) -> Bool)) -> UITableViewCell {
let cell: UITableViewCell!
if property.dataType.isBasic {
// The property is a key or we are creating new entity
if (!property.isKey || self.entity.isNew) {
// .. that CAN be edited
cell = self.cellWithEditableContent(tableView: tableView, indexPath: indexPath, property: property, with: value, with: name, changeHandler: changeHandler)
} else {
// .. that CANNOT be edited
cell = self.cellWithNonEditableContent(tableView: tableView, indexPath: indexPath, for: property.name, with: value, with: name)
}
} else {
// A complex property
cell = self.cellWithNonEditableContent(tableView: tableView, indexPath: indexPath, for: property.name, with: "...", with: name)
}
return cell
}
func cellForDefault(tableView: UITableView, indexPath: IndexPath) -> FUISimplePropertyFormCell {
let cell = tableView.dequeueReusableCell(withIdentifier: FUISimplePropertyFormCell.reuseIdentifier, for: indexPath) as! FUISimplePropertyFormCell
cell.textLabel!.text = ""
cell.textLabel!.numberOfLines = 0
cell.textLabel!.lineBreakMode = NSLineBreakMode.byWordWrapping
cell.keyName = "default"
return cell
}
private func cellWithEditableContent(tableView: UITableView, indexPath: IndexPath, property: Property, with value: String, with name: String, changeHandler: #escaping((String) -> Bool)) -> FUISimplePropertyFormCell {
let cell = tableView.dequeueReusableCell(withIdentifier: FUISimplePropertyFormCell.reuseIdentifier, for: indexPath) as! FUISimplePropertyFormCell
cell.isEditable = true
cell.keyName = name
cell.value = value
if !property.isOptional {
cell.valueTextField!.placeholder = NSLocalizedString("keyRequiredPlaceholder", value: "Required", comment: "XSEL: Placeholder text for required but currently empty textfield.")
}
cell.onChangeHandler = { (newValue) -> Void in
if !changeHandler(newValue) {
cell.valueTextField.textColor = UIColor.red
} else {
cell.valueTextField.textColor = UIColor.gray
}
}
return cell
}
private func cellWithNonEditableContent(tableView: UITableView, indexPath: IndexPath, for key: String, with value: String, with name: String) -> FUISimplePropertyFormCell {
let cell = tableView.dequeueReusableCell(withIdentifier: FUISimplePropertyFormCell.reuseIdentifier, for: indexPath) as! FUISimplePropertyFormCell
cell.keyName = name
cell.value = value
return cell
}
private func selectKeyboardFor(_ type: DataType) -> UIKeyboardType {
switch type.code {
case DataType.byte, DataType.short, DataType.integer, DataType.int:
return .decimalPad
case DataType.decimal, DataType.double, DataType.localDateTime, DataType.globalDateTime:
return .numbersAndPunctuation
default:
return .`default`
}
}
func defaultValueFor(_ property: Property) -> Double {
if let defaultValue = property.defaultValue {
return Double(defaultValue.toString())!
} else {
return Double()
}
}
func defaultValueFor(_ property: Property) -> BigDecimal {
if let defaultValue = property.defaultValue {
return (defaultValue as! DecimalValue).value
} else {
return BigDecimal.fromDouble(Double())
}
}
func defaultValueFor(_ property: Property) -> Int {
if let defaultValue = property.defaultValue {
return Int(defaultValue.toString())!
} else {
return Int()
}
}
func defaultValueFor(_ property: Property) -> BigInteger {
if let defaultValue = property.defaultValue {
return BigInteger(defaultValue.toString())
} else {
return BigInteger.fromInt(Int())
}
}
func defaultValueFor(_ property: Property) -> Int64 {
if let defaultValue = property.defaultValue {
return Int64(defaultValue.toString())!
} else {
return Int64()
}
}
func defaultValueFor(_ property: Property) -> Float {
if let defaultValue = property.defaultValue {
return Float(defaultValue.toString())!
} else {
return Float()
}
}
func defaultValueFor(_ property: Property) -> LocalDateTime {
if let defaultValue = property.defaultValue {
return LocalDateTime.parse(defaultValue.toString())!
} else {
return LocalDateTime.now()
}
}
func defaultValueFor(_ property: Property) -> GlobalDateTime {
if let defaultValue = property.defaultValue {
return GlobalDateTime.parse(defaultValue.toString())!
} else {
return GlobalDateTime.now()
}
}
func defaultValueFor(_ property: Property) -> GuidValue {
if let defaultValue = property.defaultValue {
return GuidValue.parse(defaultValue.toString())!
} else {
return GuidValue.random()
}
}
func defaultValueFor(_ property: Property) -> String {
if let defaultValue = property.defaultValue {
return defaultValue.toString()
} else {
return ""
}
}
func defaultValueFor(_ property: Property) -> Bool {
if let defaultValue = property.defaultValue {
return defaultValue.toString().toBool()!
} else {
return Bool()
}
}
}
From your description, I assume you are using a UITableViewController and not the FUIFormTableViewController. The events of “FUI*FormCells” are handled only by the FUIFormTableViewController, not by a UITableViewController.

Type 'BusinessData' has no member 'sortUsingComparator'

i am getting some data from one url and i am try to display in table view. before that i directly used all array , search, sorting.Its worked well.But now, when i create separate class for my table view i am getting this error
Type 'BusinessData' has no member 'sortUsingComparator' in my sorting function.Here i gave 3 button action.Inside that button action i declaring this sorting code.But now i am getting this error.
my bussinessData.swift class
import UIKit
class BusinessData {
var BusinessName: String?
var Address: String?
var Rating: Float?
var ContactNumber: String?
init(json: NSDictionary) {
self.BusinessName = json["business_name"] as? String
self.Address = json["location"] as? String
self.Rating = json["__v"] as? Float
self.ContactNumber = json["phone_no"] as? String
}
}
My viewcontroller.swift code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
var isTapped:Bool? // cell tap checking bool
var selectedIndex:NSIndexPath?
#IBOutlet weak var RightMenu: UIView!
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView! // UITable view declaration
var arrDict = [BusinessData]() // array to store the value from json
var isSearching:Bool?
var arrSearch:NSMutableArray=[]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.jsonParsingFromURL() // call the json method
searchBar.returnKeyType = UIReturnKeyType.Done;
searchBar.hidden = true;
// nib for custom cell (table view)
let nib = UINib(nibName:"customCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
indicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 90, 90))
indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
indicator.center = self.view.center
indicator.color = UIColor .redColor()
self.view.addSubview(indicator)
}
override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "searchMethod:", name: "search", object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: "endSearch", name: "endSearch", object: nil);
}
// web services method
func jsonParsingFromURL ()
{
let url:NSURL = NSURL(string: "http://sample url/Fes?current_location=toronto&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyIkX18")!
if let JSONData = NSData(contentsOfURL: url)
{
if let json = (try? NSJSONSerialization.JSONObjectWithData(JSONData, options: [])) as? NSDictionary
{
if let reposArray = json["data"] as? [NSDictionary]
{
for item in reposArray
{
let itemObj = item as? Dictionary<String,AnyObject>
let b_type = itemObj!["business_type"]?.valueForKey("type")
if (b_type as? String == "Taxis")
{
arrDict.append(BusinessData(json: item))
}
}
}
}
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
if(isSearching == true)
{
return arrSearch.count;
}
return self.arrDict.count
}
// number of rows
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 1
}
// height for each cell
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
return cellSpacingHeight
}
// calling each cell based on tap and users ( premium / non premium )
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell:customCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! customCell
if(isSearching == true)
{
cell.vendorName.text = arrDict[indexPath.section].BusinessName
cell.vendorAddress.text = arrDict[indexPath.section].Address
cell.VendorRating.rating = arrDict[indexPath.section].Rating!
}
else
{
cell.vendorName.text = arrDict[indexPath.section].BusinessName
cell.vendorAddress.text = arrDict[indexPath.section].Address
cell.VendorRating.rating = arrDict[indexPath.section].Rating!
}
return cell
}
// MARK:
// MARK: Sort Method
#IBAction func sortByRevBtnPress(sender: AnyObject)
{
self.indicator.startAnimating()
self.indicator.hidden = false
RightMenu.hidden = true
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(1 * NSEC_PER_SEC)), dispatch_get_main_queue()){
self.indicator.stopAnimating()
self.indicator.hidden = true
};
self.tableView.reloadData()
}
#IBAction func sortByAZBtnPress(sender: AnyObject)
{
RightMenu.hidden = true
BusinessData.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["business_name"] as? String, name2 = dict2["business_name"] as? String
{
return name1.compare(name2)
}
return .OrderedAscending
}
self.tableView.reloadData()
}
#IBAction func sortByRatingBtnPress(sender: AnyObject)
{
RightMenu.hidden = true
BusinessData.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["rating"] as? String, name2 = dict2["rating"] as? String
{
return name2.compare(name1)
}
return .OrderedDescending
}
self.tableView.reloadData()
}
#IBAction func sortByRecentBtnPress(sender: AnyObject)
{
RightMenu.hidden = true
BusinessData.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["created_at"] as? String, name2 = dict2["created_at"] as? String
{
return name2.compare(name1)
}
return .OrderedDescending
}
self.tableView.reloadData()
}
// MARK:
// MARK: Search Method
func endSearch()
{
if(isSearching == false)
{
isSearching = true;
searchBar.hidden = false;
yConstraint.constant = 47;
self.view.layoutIfNeeded();
}
else
{
isSearching = false;
searchBar.hidden = true;
tableView.reloadData();
yConstraint.constant = 3;
self.view.layoutIfNeeded();
self.view.endEditing(true);
}
}
func searchMethod(notification:NSNotification)
{
if(isSearching == true)
{
isSearching = false;
searchBar.hidden = true;
tableView.reloadData();
yConstraint.constant = 3;
self.view.layoutIfNeeded();
self.view.endEditing(true);
}
else
{
isSearching = true;
searchBar.hidden = false;
yConstraint.constant = 47;
self.view.layoutIfNeeded();
}
}
// MARK:
// MARK: SearchBar Method
func searchBar(searchBar: UISearchBar, textDidChange searchText: String)
{
arrSearch = [];
for(var i=0;i<arrDict.count;i++)
{
if((BusinessData.objectAtIndex(i).objectForKey("name")?.lowercaseString?.containsString(searchText.lowercaseString)) == true)
{
arrSearch.addObject(BusinessData.objectAtIndex(i));
}
}
tableView.reloadData();
}
func searchBarSearchButtonClicked(searchBar: UISearchBar)
{
self.view.endEditing(true);
isSearching = false;
searchBar.hidden = true;
tableView.reloadData();
yConstraint.constant = 3;
self.view.layoutIfNeeded();
}
}
I am getting error in this these button action method:
#IBAction func sortByAZBtnPress(sender: AnyObject)
{
RightMenu.hidden = true
BusinessData.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["business_name"] as? String, name2 = dict2["business_name"] as? String
{
return name1.compare(name2)
}
return .OrderedAscending
}
self.tableView.reloadData()
}
#IBAction func sortByRatingBtnPress(sender: AnyObject)
{
RightMenu.hidden = true
BusinessData.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["rating"] as? String, name2 = dict2["rating"] as? String
{
return name2.compare(name1)
}
return .OrderedDescending
}
self.tableView.reloadData()
}
#IBAction func sortByRecentBtnPress(sender: AnyObject)
{
RightMenu.hidden = true
BusinessData.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["created_at"] as? String, name2 = dict2["created_at"] as? String
{
return name2.compare(name1)
}
return .OrderedDescending
}
self.tableView.reloadData()
}
In every method i am getting this error Type 'BusinessData' has no member 'sortUsingComparator'..In this below line :
BusinessData.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
Please help me out
Thanks
BusinessData is a model class. It looks like what you really want to do is to sort var arrDict = [BusinessData]() - an array of BusinessData instances.
Your current code BusinessData.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in is trying to call a class method sortUsingComparator on BusinessData model class and expects to get 2 dictionaries back.
What you want is something more like:
arrDict.sortUsingComparator { (b1, b2) -> NSComparisonResult in
where b1 and b2 are instances of BusinessData, not dictionaries.

Resources