Why is my NSTableView displaying my data wrong? - swift

My NSTableView seems to be mirroring all content which draws a String.
I have never seen something like this before and hope somebody has a tip on how to solve this Problem. I already looked it up, but couldn't find anything. I also filed a bug report, but apple didn't respond.
First idea, that I have: It must have something to do with the NSTextField and NSPopUpButton being disabled at start. They are only enabled as soon as you click one cell. And when they are enabled the text gets displayed the right way. But I don't want to enable them at start to prevent changing values by accidentally clicking one cell.
My Code seems to be fine and compiled without problems.
The Program is a simple Database Program which takes an own created file type and reads its content. From the content it creates Database, Table, Column and cell objects at runtime to display the database content.
Here is my NSTableView Code:
import Cocoa
class TableContentViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet weak var tableContent: NSTableView!
var columnDragFrom:Int = -1
override func viewDidLoad() {
super.viewDidLoad()
tableContent.dataSource = self
tableContent.delegate = self
// Do view setup here.
tableContent.backgroundColor = NSColor(named: "darkColor")!
NotificationCenter.default.addObserver(self, selector: #selector(reloadData(_:)), name: .tableUpdated, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(cellSelection(_:)), name: .cellSelection, object: nil)
tableContent.selectionHighlightStyle = .none
}
#objc func cellSelection(_ notification: Notification) {
if let cell = notification.object as? NSTableCellView {
nxSelectionHandler.currentRow = tableContent.row(for: cell)
nxSelectionHandler.currentColumn = tableContent.column(for: cell)
nxSelectionHandler.highlightCell(sender: tableContent)
}
}
#objc func reloadData(_ notification: Notification) {
setupTable()
tableContent.reloadData()
}
func setupTable() {
tableContent.rowHeight = 30
while(tableContent.tableColumns.count > 0) {
tableContent.removeTableColumn(tableContent.tableColumns.last!)
}
if nxSelectionHandler.currentTable != nil {
for column in (nxSelectionHandler.currentTable?.nxColumns)! {
let newColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: column.title))
newColumn.title = column.title
tableContent.addTableColumn(newColumn)
}
}
}
func numberOfRows(in tableView: NSTableView) -> Int {
if nxSelectionHandler.currentTable != nil {
var rowCounts:[Int] = []
for column in (nxSelectionHandler.currentTable?.nxColumns)! {
rowCounts.append(column.nxCells.count)
}
return rowCounts.max()!
}
return 0
}
func tableView(_ tableView: NSTableView, mouseDownInHeaderOf tableColumn: NSTableColumn) {
self.columnDragFrom = tableView.tableColumns.firstIndex(of: tableColumn)!
}
func tableView(_ tableView: NSTableView, didDrag tableColumn: NSTableColumn) {
nxSelectionHandler.currentTable?.nxColumns.swapAt(columnDragFrom, tableView.tableColumns.firstIndex(of: tableColumn)!)
tableView.reloadData()
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let column = tableView.tableColumns.firstIndex(of: tableColumn!)
if nxSelectionHandler.currentTable != nil {
let nxCell = nxSelectionHandler.currentTable?.nxColumns[column!].nxCells[row]
switch nxCell! {
case .nxString(let value):
var StringCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "nxString"), owner: self) as? StringCell
if StringCellView == nil {
tableView.register(NSNib(nibNamed: "StringCellNib", bundle: nil), forIdentifier: NSUserInterfaceItemIdentifier(rawValue: "nxString"))
StringCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "nxString"), owner: self) as? StringCell
}
StringCellView?.textField?.stringValue = value
return StringCellView
case .nxCheckbox(let state):
var CheckboxCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "nxCheckbox"), owner: self) as? CheckboxCell
if CheckboxCellView == nil {
tableView.register(NSNib(nibNamed: "CheckboxCellNib", bundle: nil), forIdentifier: NSUserInterfaceItemIdentifier("nxCheckbox"))
CheckboxCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "nxCheckbox"), owner: self) as? CheckboxCell
}
CheckboxCellView?.column = column!
CheckboxCellView?.row = row
CheckboxCellView?.checkbox.state = state
return CheckboxCellView
case .nxSelection(let selection, let options):
var SelectionCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "nxSelection"), owner: self) as? SelectionCell
if SelectionCellView == nil {
tableView.register(NSNib(nibNamed: "SelectionCellNib", bundle: nil), forIdentifier: NSUserInterfaceItemIdentifier(rawValue: "nxSelection"))
SelectionCellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "nxSelection"), owner: self) as? SelectionCell
}
SelectionCellView?.column = column!
SelectionCellView?.row = row
for option in options {
SelectionCellView?.selection.addItem(withTitle: option)
}
SelectionCellView?.selection.selectItem(at: selection)
return SelectionCellView
}
}
return nil
}
}
The objects used in the Code are all class types and cells are loaded from Nibs, where the cells all have constraints and are displayed the right way. A Screenshot of the NSTableView displaying the content wrong can be seen below.
Code of one of the custom cells:
import Cocoa
class StringCell: NSTableCellView, NSTextFieldDelegate {
var isSelected: Bool = false {
didSet {
self.needsDisplay = true
}
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
self.textField?.focusRingType = .none
self.textField?.textColor = NSColor.white
self.textField?.delegate = self
self.wantsLayer = true
self.layer?.borderWidth = 2
self.layer?.cornerRadius = 2
if isSelected {
self.layer?.borderColor = NSColor.systemBlue.cgColor
} else {
self.layer?.borderColor = NSColor.clear.cgColor
self.textField?.isEnabled = false
self.textField?.isEditable = false
}
}
override func mouseDown(with event: NSEvent) {
if self.isSelected {
self.textField?.isEditable = true
self.textField?.isEnabled = true
self.textField?.selectText(self)
} else {
self.isSelected = true
NotificationCenter.default.post(name: .cellSelection, object: self)
}
}
func controlTextDidEndEditing(_ obj: Notification) {
if let textField = obj.object as? NSTextField {
nxSelectionHandler.currentCell = nxCell.nxString(textField.stringValue)
}
}
}

Yeah, that's weird. Did you check if you have any active content filters in the View Effects inspector?
View Effects inspector

Related

How to show one tableview data in two segments in swift

I am using XLPagerTabStrip for segment
i want to show one tableview data in two segment with sorted, for that i am using below code
SegmentViewController code: if i add child_2.isSorted = false then error
Value of type 'UIViewController' has no member 'isSorted'
import UIKit
import XLPagerTabStrip
class SegmentViewController: ButtonBarPagerTabStripViewController {
#IBOutlet weak var testSeg: ButtonBarView!
override func viewDidLoad() {
super.viewDidLoad()
// change selected bar color
// Sets the background colour of the pager strip and the pager strip item
settings.style.buttonBarBackgroundColor = .white
settings.style.buttonBarItemBackgroundColor = .yellow
// Sets the pager strip item font and font color
settings.style.buttonBarItemFont = UIFont(name: "Helvetica", size: 15.0)!
settings.style.buttonBarItemTitleColor = .gray
changeCurrentIndexProgressive = { [weak self] (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in
guard changeCurrentIndex == true else { return }
oldCell?.label.textColor = .gray
newCell?.label.textColor = .blue
}
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.buttonBarView.frame = testSeg.frame
}
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
let child_1 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegTableViewController")
let child_2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegTableViewController")
child_2.isSorted = false
return [child_1, child_2]
}
}
SegTableViewController code: here in both segments showing only sortedArray i need in first segment sortedArray and second segment namesArray.. how to do that.. could anyone please suggest me
import UIKit
import XLPagerTabStrip
class SegTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, IndicatorInfoProvider {
var sortedArray = [String]()
var isSorted = true
func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo {
if isSorted{
return IndicatorInfo(title: "Sorted")
}else{
return IndicatorInfo(title: "Normal")
}
}
#IBOutlet weak var tableView: UITableView!
var namesArray = ["afsdf","ddsfsdf", "hjhgjh", "trytryr", "nvbmvnb", "yuertyri", "bmvmncb", "jgfhk", "ytuioi", "sdfgsfdsh", "mkhjgijik", "gfuyru"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
sortedArray = namesArray.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedDescending }
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSorted {
return sortedArray.count
}
else{
return namesArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
if isSorted{
cell.textLabel?.text = sortedArray[indexPath.row]
}
else{
cell.textLabel?.text = namesArray[indexPath.row]
}
return cell
}
}
You need to cast your view controller like this
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
let child_1 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegTableViewController")
let child_2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegTableViewController")
(child_2 as? SegTableViewController).isSorted = false
return [child_1, child_2]
}
Also, no need for an array for displaying data. Use only one array namesArray like this
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if isSorted {
namesArray = namesArray.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedDescending }
}
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}

how to call/show action sheet for taking or choose photo from gallery on Main VC after tapped on TableViewCell Image(swift)

Here I Try MVVM pattern to achieve TableView but for showing alert I face problems , it compiles successfully but not showing alert.
[Result] on the tapped on profile pic which is in tableview cell, I want to show alert
TableViewCell
import Foundation
import UIKit
class ParentsImageCell : UITableViewCell {
weak var myVC : ProfileOfParentsDetailsViewController?
var parentProfileVC = ProfileOfParentsDetailsViewController()
#IBOutlet weak var imageProfile : UIImageView!
var items : ParentProfileViewModelItem? {
didSet {
guard let items = items as? ParentProfileViewModelProfileItem else {
return
}
imageProfile?.image = UIImage(named: items.profileImg)
}
}
static var nib:UINib {
return UINib(nibName: identifier, bundle: nil)
}
static var identifier: String {
return String(describing: self)
}
override func awakeFromNib() {
super.awakeFromNib()
imageProfile?.layer.cornerRadius = 62
imageProfile?.clipsToBounds = true
imageProfile?.contentMode = .scaleAspectFill
imageProfile?.backgroundColor = UIColor.lightGray
//Add Tapped Gesture
imageProfile.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(
target: self,
action: #selector(didTappedChangeProfilePic))
gesture.numberOfTapsRequired = 1
gesture.numberOfTouchesRequired = 1
imageProfile.addGestureRecognizer(gesture)
}
#objc private func didTappedChangeProfilePic(){
print("tapped on imageView")
presentPhotoActionSheet()
}
override func prepareForReuse() {
super.prepareForReuse()
imageProfile?.image = nil
}
}
extension ParentsImageCell : UIImagePickerControllerDelegate ,UINavigationControllerDelegate {
func presentPhotoActionSheet(){
let actionSheet = UIAlertController(title: "Profile Picture", message: "How would you write to select a picture", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
actionSheet.addAction(UIAlertAction(title: "Take Photo", style: .default, handler: {[weak self] _ in
self?.presentCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Choose Photo", style: .default, handler: { [weak self]_ in
self?.presentPhotoPicker()
}))
myVC?.present(actionSheet , animated: true)
}
func presentCamera(){
let vc = UIImagePickerController()
vc.sourceType = .camera
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func presentPhotoPicker(){
let vc = UIImagePickerController()
vc.sourceType = .photoLibrary
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:
[UIImagePickerController.InfoKey : Any]) {
guard let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage else {
return
}
self.imageProfile.image = selectedImage
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
ViewModel
class ParentProfileViewModel: NSObject {
var items = [ParentProfileViewModelItem]()
var reloadSections: ((_ section: Int) -> Void)?
override init() {
super.init()
guard let data = dataFromFile("ServerData"),
let profile = Profile(data: data) else {
return
}
// initialization code will go here
if let profile = profile.pictureUrl {
let profileItem = ParentProfileViewModelProfileItem(profileImg: profile)
items.append(profileItem)
}
if let name = profile.fullName {
let nameItem = ParentProfileViewModelNameItem(name: name)
items.append(nameItem)
}
if let email = profile.email {
let emailItem = ParentProfileViewModelEmailItem(email: email)
items.append(emailItem)
}
let coach = profile.coach
if !coach.isEmpty {
let coachItem = ParentProfileViewModelCoachItem(coach: coach)
items.append(coachItem)
}
let candidate = profile.candidate
if !candidate.isEmpty {
let candidateItem = ParentProfileViewModelCandidateItem(candidate: candidate)
items.append(candidateItem)
}
}
}
//MARK:- TableviewDatasource & Delegates
extension ParentProfileViewModel: UITableViewDataSource {
//Number of section
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
//Number of RowInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let item = items[section]
guard item.isCollapsible else {
return item.rowCount
}
if item.isCollapsed {
return 0
} else {
return item.rowCount
}
}
//Cell for row at indexpath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// we will configure the cells here
let item = items[indexPath.section]
switch item.type {
case .profileImg:
let vc = ProfileOfParentsDetailsViewController()
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsImageCell.identifier, for: indexPath) as? ParentsImageCell {
cell.items = item
cell.myVC = vc
return cell
}
case .fullName:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsFulNameCell.identifier, for: indexPath) as? ParentsFulNameCell {
cell.items = item
return cell
}
case .email:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsEmailCell.identifier, for: indexPath) as? ParentsEmailCell {
cell.items = item
return cell
}
case .candidate:
if let item = item as? ParentProfileViewModelCandidateItem, let cell = tableView.dequeueReusableCell(withIdentifier: CandidatCell.identifier, for: indexPath) as? CandidatCell {
let candidate = item.candidate[indexPath.row]
cell.item = candidate
return cell
}
case .coach:
if let item = item as? ParentProfileViewModelCoachItem, let cell = tableView.dequeueReusableCell(withIdentifier: ParentCoachCell.identifier, for: indexPath) as? ParentCoachCell {
cell.item = item.coach[indexPath.row]
return cell
}
}
return UITableViewCell()
}
}
ViewController
import Foundation
import UIKit
class ProfileOfParentsDetailsViewController: UIViewController {
let viewModel = ParentProfileViewModel()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
viewModel.reloadSections = { [weak self] (section: Int) in
self?.tableView?.beginUpdates()
self?.tableView?.reloadSections([section], with: .fade)
self?.tableView?.endUpdates()
}
tableView?.dataSource = viewModel
tableView.delegate = viewModel
tableView?.estimatedRowHeight = 250
tableView?.rowHeight = UITableView.automaticDimension
tableView?.register(ParentsImageCell.nib, forCellReuseIdentifier: ParentsImageCell.identifier)
tableView?.register(ParentsEmailCell.nib, forCellReuseIdentifier: ParentsEmailCell.identifier)
tableView?.register(ParentsFulNameCell.nib, forCellReuseIdentifier: ParentsFulNameCell.identifier)
tableView?.register(CandidatCell.nib, forCellReuseIdentifier: CandidatCell.identifier)
tableView?.register(ParentCoachCell.nib, forCellReuseIdentifier: ParentCoachCell.identifier)
tableView?.register(ParentsHeaderView.nib, forHeaderFooterViewReuseIdentifier: ParentsHeaderView.identifier)
}
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM

NSOutlineView unexpectedly found nil

I am trying to make a file browser in my app that opens in a side panel (with a split view controller).
The source is a URL brought by a prepareForSegue method in the previous viewController.
Each time the vc loads i have the fatal error :
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
The compiler locates the error to where i declare :
outlineView.delegate = self
outlineView.dataSource = self
I tried :
1. Undoing and redoing all my outlets connections, by code, by
storyboard
2. Reconnecting delegates and datasource by code, by storyboard
3. I thought maybe something was wrong in my datasource method and i rewrote it 5 times
4. I tried to put my setDelegatesAndDatasource method in the viewDidAppear too, thinking it was a problem of view life cycle
I can't understand what's going on.
Thanks for your help.
'''
extension ViewControllerSource : NSOutlineViewDataSource, NSOutlineViewDelegate {
func setDelegatesAndDatasources(){
outlineView.delegate = self
outlineView.dataSource = self
}
// MARK: - NSOutlineView Datasource
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if let fileSystemItem = item as? FileSystemItem {
return fileSystemItem.children.count
}
return 1
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
if let fileSystemItem = item as? FileSystemItem {
return fileSystemItem.children[index]
}
return rootfileSystemItem
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
if let fileSystemItem = item as? FileSystemItem {
return fileSystemItem.hasChildren()
}
return false
}
// MARK: - NSOutlineView Delegate
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
guard let colIdentifier = tableColumn?.identifier else { return nil }
if colIdentifier == NSUserInterfaceItemIdentifier(rawValue: "col1") {
let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "cell1")
guard let cell = outlineView.makeView(withIdentifier: cellIdentifier, owner: nil) as? NSTableCellView else { return nil }
if let collection = item as? FileSystemItem {
cell.textField?.stringValue = collection.name ?? "Title not available"
cell.textField?.isEditable = false
cell.textField?.wantsLayer = true
cell.imageView?.image = collection.icon
// cell.textField?.delegate = self
} else {
cell.textField?.stringValue = "unknown item"
cell.textField?.isEditable = false
cell.textField?.wantsLayer = true
}
return cell
} else {
return nil
}
}
}
'''
And here is the main viewController file :
'''
class ViewControllerSource: NSViewController {
#IBOutlet var outlineView: NSOutlineView!
var echo:Echo? {
didSet {
echo!.checkFolderIntegrity()
rootfileSystemItem = FileSystemItem(url: echo!.url)
let window = self.view.window?.windowController as! WindowControllerEcho
window.directoryPath.url = echo!.url
}
}
let propertyKeys: [URLResourceKey] = [.localizedNameKey, .effectiveIconKey, .isDirectoryKey, .typeIdentifierKey]
var rootfileSystemItem: FileSystemItem! {
didSet {
displayItems()
outlineView.reloadData()
}
}
// MARK: - Initialization
override func viewDidLoad() {
super.viewDidLoad()
setDelegatesAndDatasources()
}
func displayItems(){
for fileSystemItem in rootfileSystemItem.children as [FileSystemItem] {
print("item : \(fileSystemItem)")
for subItem in fileSystemItem.children as [FileSystemItem] {
print("\(fileSystemItem.name) - \(subItem.name)")
}
}
}
}
extension ViewControllerSource : EchoDelegate {
func didLoad(echo: Echo) {
self.echo = echo
}
}
'''
The prepareForSegue code reveals the mistake:
You are setting echo in prepareForSegue. This causes to call the property observer didSet. However at this moment the view is not loaded yet and force unwrapping the type crashes.
The solution is to move the code in didSet into viewDidLoad and viewWillAppear and delete the property observer. Nevertheless I recommend to optional bind window
var echo : Echo!
override func viewDidLoad() {
super.viewDidLoad()
setDelegatesAndDatasources()
echo.checkFolderIntegrity()
rootfileSystemItem = FileSystemItem(url: echo.url)
}
override func viewWillAppear(_ animated : Bool) {
super.viewWillAppear(animated)
if let window = self.view.window?.windowController as? WindowControllerEcho {
window.directoryPath.url = echo.url
}
}
Setting delegate and dataSource once is sufficient. If you are using storyboard or Xib the most convenient way is to connect both in Interface Builder.

Autolayout Contraints Changing When View Is Reloaded - Swift

recently in the search VC of my app I have been having a major problem with the contraints for weeks. I've redone them 4 times now with no improvement. My tableview cell is not complex -- an imageView located at the left and two labels on top of each other. Most cells size fine. The only thing is that(without pattern) two cells are randomly messed up(the labels and imageView move way out of place). You can see here:
Here is a picture of the constraints for the vc:
The strange thing is that this sometimes occurs when I toggle the scopeButtons up top. I looked in my code to see if I could fix this when the scopeButton reloads the tableview, but I could not find a problematic instance. My code is below:
import UIKit
import Firebase
import Kingfisher
class SearchPostsController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var idArray:[String] = []
var detailViewController: DetailViewController? = nil
var candies = [Person]()
var filteredCandies = [Person]()
let searchController = UISearchController(searchResultsController: nil)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if #available(iOS 11.0, *) {
navigationItem.hidesSearchBarWhenScrolling = false
//initiate the search bar that appears up top when view is segued to
}
if let selectionIndexPath = self.tableView.indexPathForSelectedRow {
self.tableView.deselectRow(at: selectionIndexPath, animated: animated)
}
self.tableView.reloadData()
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 11.0, *) {
navigationItem.hidesSearchBarWhenScrolling = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
Database.database().reference()
.child("\(UserData().mySchool!)/posts")
.queryOrderedByKey()//keys were out of order, so we have to use this to help
.observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot.childrenCount)
for child in snapshot.children.allObjects as! [DataSnapshot] {
print(child.key)
self.idArray.append(child.key)
}
var reversedNames = [String]()
for arrayIndex in 0..<self.idArray.count {
reversedNames.append(self.idArray[(self.idArray.count - 1) - arrayIndex])
//reverse names so we dont have to sort the cells by date
}
for x in reversedNames{
self.searchNames(id: x)//get names from the ids here, tada!!!
self.tableView.reloadData()
}
})
//self.tableView.reloadData()//without this, the results wouldnt show up right away
searchController.searchBar.setScopeBarButtonTitleTextAttributes([NSAttributedStringKey.foregroundColor.rawValue: UIColor.white], for: .normal)
searchController.searchBar.scopeButtonTitles = ["Posts", "Users"]
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search posts or usernames"
searchController.searchBar.showsScopeBar = true
navigationItem.searchController = searchController
definesPresentationContext = true
if let splitViewController = splitViewController {
let controllers = splitViewController.viewControllers
detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 74
}
// override func viewWillDisappear(_ animated: Bool) {
// candies.removeAll()
// }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DAY69:GRADUATION DAY", for: indexPath) as! SeachCell
cell.cellImageVIew.frame.size.width = 48
cell.cellImageVIew.frame.size.height = 48
let personUser: Person
if isFiltering() {
personUser = filteredCandies[indexPath.row]
} else {
personUser = candies[indexPath.row]
}
//PERSON USER IS IMPORTANT!!!!! ^^^
cell.nameLabel.text = personUser.name.removingPercentEncoding
cell.messageLabel.text = personUser.category.removingPercentEncoding
let name = personUser.name
Database.database().reference().child("users/\(name)/profileImageURL").observe(.value, with: { (snapshot) in
let profURL = "\(snapshot.value!)"
let profIRL = URL(string: profURL)
//set up imageview
cell.cellImageVIew.layer.borderWidth = 1
cell.cellImageVIew.layer.masksToBounds = false
cell.cellImageVIew.layer.borderColor = UIColor.black.cgColor
cell.cellImageVIew.layer.cornerRadius = cell.cellImageVIew.frame.height/2
cell.cellImageVIew.clipsToBounds = true
cell.cellImageVIew.contentMode = .scaleAspectFill
cell.cellImageVIew.kf.indicatorType = .activity
cell.cellImageVIew.kf.setImage(with: profIRL)
})
//TODO: make an extension of imageview to do all this for me. It's getting to be ridiculous
return cell
}
func searchNames(id: String){
// var message = String()
// var name = String()
Database.database().reference().child("\(UserData().mySchool!)/posts/\(id)/message").observe(.value, with: { (snapshot) in
// message = snapshot.value as! String
Database.database().reference().child("\(UserData().mySchool!)/posts").child("\(id)/username").observe(.value, with: { (username) in
// name = username.value as! String
let user = Person(category: "\(snapshot.value!)", name: "\(username.value!)", id: id)
self.candies.append(user)
print( "\(snapshot.value!)", "\(username.value!)")
self.tableView.reloadData()
})
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
return filteredCandies.count
}
return candies.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
tableView.estimatedRowHeight = 74.0
tableView.rowHeight = UITableViewAutomaticDimension
return UITableViewAutomaticDimension
}
// func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
// return 74
// }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "DAY69:GRADUATION DAY", for: indexPath) as! SeachCell
cell.cellImageVIew.frame.size.width = 48
cell.cellImageVIew.frame.size.height = 48
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles?[searchBar.selectedScopeButtonIndex]
if scope == "Users"{
let username = candies[indexPath.row].name
print(username)
self.tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "userClicked", sender: username)
}
if scope == "Posts"{
let post = candies[indexPath.row].category
let user = candies[indexPath.row].name
let id = candies[indexPath.row].id
print(post)
let defaults = UserDefaults.standard
defaults.set(id, forKey: "ID")
let def2 = UserDefaults.standard
def2.set(post, forKey: "Post")
def2.set(user, forKey: "USER")
self.tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "postCellTapped", sender: nil)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//__________tbv methods above________________________________________________
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredCandies = candies.filter({(candy : Person) -> Bool in
let doesCategoryMatch = (scope == "Posts") || (scope == "Users")
print(searchText)
if searchBarIsEmpty() {
return doesCategoryMatch
}
if scope == "Users"{
return doesCategoryMatch && candy.name.lowercased().contains(searchText.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!.lowercased())
}
else{
return doesCategoryMatch && candy.category.lowercased().contains(searchText.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!.lowercased())
}
})
tableView.reloadData()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let personalUser: Person
if isFiltering() {
personalUser = filteredCandies[indexPath.row]
} else {
personalUser = candies[indexPath.row]
}
}
}
if segue.identifier == "userClicked" {
if let nextView = segue.destination as? UserProfileController {
nextView.selectedUser = "\(sender!)"
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func isFiltering() -> Bool {
let searchBarScopeIsFiltering = searchController.searchBar.selectedScopeButtonIndex != 0
return searchController.isActive && (!searchBarIsEmpty() || searchBarScopeIsFiltering)
}
}
extension SearchPostsController: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}
extension SearchPostsController: UISearchBarDelegate {
// MARK: - UISearchBar Delegate
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
Any help would be greatly appreciated in trying to fix the constraints! Thanks!

Columns added programmatically to NSTableView not recognised in Delegate

I may be lost in a glass of water but I can't seem to be able to add columns to a NSTableView that are then recognised in the NSTableViewDelegate. I create a table in IB with one column and give the column a string identifier. The I add the other columns in the View Controller:
override func viewDidLoad() {
super.viewDidLoad()
for columnIndex in 0..<blotter!.singleOutput[0].parameter.count {
let tmpParam = blotter!.singleOutput[0].parameter[columnIndex]
let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: tmpParam.columnID))
column.title = tmpParam.label
column.width = CGFloat(80)
column.minWidth = CGFloat(40)
column.maxWidth = CGFloat(120)
blotterOutputTable.addTableColumn(column)
}
blotterOutputTable.delegate = self
blotterOutputTable.dataSource = self
blotterOutputTable.target = self
blotterOutputTable.reloadData()
}
The NSTableViewDataSource returns the correct number of rows. The problem I have is in the NSTableViewDelegate:
extension OutputsViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var text: String = ""
var cellIdentifier: String = ""
guard let item = blotter?.singleOutput[row] else { return nil }
// 1. LABELS COLUMN
// ================
if tableColumn?.identifier.rawValue == "dealColumn" {
let myParameter = item.parameter.index(where: {$0.columnID == "BBTickColumn"})
text = item.parameter[myParameter!].value as! String
cellIdentifier = "dealColumn"
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {
cell.textField?.stringValue = text
return cell
}
else { return nil }
} // END OF LABLES COLUMN (FIRST ONE)
else { // THIS IS WHERE THE PROBLEM IS
let myParameter = item.parameter.index(where: {$0.columnID == tableColumn?.identifier.rawValue } )
let (_, valueAsText) = item.parameter[myParameter!].interfaceItems()
text = valueAsText
cellIdentifier = item.parameter[myParameter!].columnID
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {
cell.textField?.stringValue = text
return cell
}
else { return nil } // DEBUGGER PARAMETER ARE FROM HERE
}
}
}
The first column is the one I created in IB with its identifier. That works. The problem I have is in the else statement (which does not check for a column identifier). Below are the parameters as I see them in the debugger window when I stop the program after the cell creation failed
tableView NSTableView 0x000000010ebf9df0
tableColumn NSTableColumn? 0x0000600000895770
row Int 0
self DataBaseManager.OutputsViewController 0x0000600000102b50
text String "FLAT"
cellIdentifier String "directionColumn"
item DataBaseManager.BlotterOutputs 0x000060000002c240
myParameter Array.Index? 0
valueAsText String "FLAT"
cell (null) (null) (null)
tableColumn NSTableColumn? 0x0000600000895770
tableColumn?.identifier NSUserInterfaceItemIdentifier? some
_rawValue _NSContiguousString "directionColumn" 0x000060000104d200
Swift._SwiftNativeNSString _SwiftNativeNSString
_core _StringCore
You can see that cellIdentifier and the tableColumn?.identifier.rawvalue are the same string (as it should be). I cannot understand then why the cell is not created. Any help is mostly welcome and let me know if this is not clear. Thanks
must register nibs identifiers as in this code:
import Cocoa
class MultiColumnTable: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
var list = [[String]](), header=[String]()
var tableView : NSTableView? = nil
var nColumns : Int = 0
func genID(col : Int) -> NSUserInterfaceItemIdentifier { // generate column ID
return NSUserInterfaceItemIdentifier(rawValue: String(format: "Col%d", col))
}
func setContent(header: [String], list : [[String]]) {
self.header = header
self.list = list
self.nColumns = list[0].count
if tableView != nil {
tableView?.reloadData()
}
}
func numberOfRows(in tableView: NSTableView) -> Int {
func createColumns() {
func addColumn(col:Int, header:String) {
let tableColumn = NSTableColumn(identifier: genID(col: col))
tableColumn.headerCell.title = header
self.tableView!.addTableColumn(tableColumn)
}
// create columns and register them in NIB
// IB: tableColumn[0] identifier ( NSTableColumn to "Col0" )
if let myCellViewNib = tableView.registeredNibsByIdentifier![NSUserInterfaceItemIdentifier(rawValue: "Col0")] {
for col in 0..<nColumns { // table should have 1 col in IB w/Ident 'Col0'
addColumn(col: col, header: header[col])
tableView.register(myCellViewNib, forIdentifier: genID(col: col)) // register the above Nib for the newly added tableColumn
}
tableView.removeTableColumn(tableView.tableColumns[0]) // remove the original Col0
}
}
self.tableView = tableView
createColumns()
return list.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
tableColumn?.headerCell.title=header[column];
if let cell = tableView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: self) as? NSTableCellView {
cell.textField?.stringValue = list[row][column]
cell.textField?.textColor = NSColor.blue
return cell
}
return nil
}
func tableViewSelectionDidChange(_ notification: Notification) {
}
}