What is the method for the NSSearchField TextDidChange? - swift

What is the analogous method for the function searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) in macOS?
Got the answer:
#IBOutlet weak var searchBar: NSSearchField!
func controlTextDidChange(_ obj: Notification){
let searchObject:NSSearchField? = obj.object as? NSSearchField
if searchObject==self.searchBar{
}
}

The equivalent is the delegate method controlTextDidChange:
The object of the notification is the NSSearchfield. You have to cast the type
#IBOutlet weak var searchBar: NSSearchField!
func controlTextDidChange(_ notification : Notification){
guard let field = notification.object as? NSSearchField, field == self.searchBar else { return }
}

You need to add the controlTextDidChange method as shown in the pic below(last method) then the searchFieldDidEndSearching
keeps track of when you finish searching or if searchbar is empty and the searchFieldDidStartSearching(_ sender: NSSearchField) gets called when you just start editing the textfield after being empty
DispatchQueue.main.async {
self.searchTable.reloadData()
}
}
func searchFieldDidStartSearching(_ sender: NSSearchField) {
}
func controlTextDidChange(_ obj: Notification) {
guard let field = obj.object as? NSSearchField, field == self.searchBar else { return }
filteredPokemonList = allPokemonList.filter({ (pokemon) -> Bool in
return pokemon.name.lowercased().contains(field.stringValue.lowercased())
})
DispatchQueue.main.async {
self.searchTable.reloadData()
}
}
below is a pic of my Attribute Inspector to have a function called based on each letter entered to filter and reload the NSTableView

Related

Save textview to CoreData when text change

I'm building a note-taking app for macOS and have a question about saving text whenever the user is editing the NSTextView. I use textDidChange function to detect any changes in the NSTextView and then save the changes to Core Data. However, my code will only save the first edit that the user makes, e.g. if I type hello in the NSTextView, it will only save h instead of hello.
I'm wondering how to fix it? Thank you for your help.
This is the code:
class ViewController: NSViewController, NSTextViewDelegate, NSTableViewDataSource, NSTableViewDelegate {
#IBOutlet var textArea: NSTextView!
#IBOutlet weak var noteList: NSTableView!
let context = (NSApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var notes = [Testnote]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
loadData()
textArea.textStorage?.setAttributedString(notes[0].noteItem!)
textArea.delegate = self
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
// MARK: - Tableview stuff
func numberOfRows(in tableView: NSTableView) -> Int {
return notes.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "rowNo"), owner: self) as? NSTableCellView {
cell.textField?.stringValue = "sth here"
return cell
}
return nil
}
func tableViewSelectionDidChange(_ notification: Notification) {
if noteList.selectedRow >= 0 {
let selectedNote = notes[noteList.selectedRow]
textArea.textStorage?.setAttributedString(selectedNote.noteItem!)
}
}
func textDidChange(_ notification: Notification) {
if let textview = notification.object as? NSTextView {
notes[0].noteItem = textview.attributedString()
saveData()
}
}
func loadData() {
let request: NSFetchRequest<Testnote> = Testnote.fetchRequest()
do {
notes = try context.fetch(request)
} catch {
print("sth wrong")
}
}
func saveData() {
do {
try context.save()
} catch {
print("Error saving \(error)")
}
}
#IBAction func addButton(_ sender: Any) {
// Create new NSObject and assign values
let newnote = Testnote(context: context)
newnote.noteItem = textArea.attributedString()
(NSApplication.shared.delegate as? AppDelegate)?.saveAction(nil)
}
#IBAction func delete(_ sender: NSButton) {
context.delete(notes[noteList.selectedRow])
do {
try context.save()
} catch {
print("Error")
}
}
}

How to fix "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" while using tableview.reloaddata

I'm creating an app where I use a NSOpenPanel, linked to a File --> Open... menu. When the user select a .txt file, the program reads it, add the values to an existing array of strings and then SHOULD reload the data in the TableView. But when I call the tableview.reloaddata(), at run time, it give me the following error: Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value.
Here the code of my AppDelegate.swift file:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let vc = ViewController(nibName: "ViewController", bundle: nil)
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
#IBAction func openUnFichier(_ sender: NSMenuItem) {
let fichierPanel: NSOpenPanel = NSOpenPanel()
fichierPanel.allowsMultipleSelection = false
fichierPanel.canChooseFiles = true
fichierPanel.canChooseDirectories = false
fichierPanel.allowedFileTypes = ["txt"]
let response = fichierPanel.runModal()
if response == NSApplication.ModalResponse.OK{
guard let selectedURL = fichierPanel.url else{return}
do{
var fullDocument = try String(contentsOf: selectedURL, encoding: String.Encoding.utf8)
print(type(of: fullDocument))
var lines : [String] = fullDocument.components(separatedBy: "\n" as String)
for line in lines {
vc.test_data.append(line)
print(type(of: vc.test_data))
}
} catch let error as NSError{
print("Erreur!!!!!!! \(error)")
}
vc.tableView.reloadData() //IT CRASHES HERE
}else {
}
}
}
And here is the code of my ViewController.swift:
import Cocoa
import WebKit
class ViewController: NSViewController, NSTableViewDataSource{
public var test_data = ["https://www.youtube.com/watch?v=0AQFQMeOAig", "https://www.youtube.com/watch?v=domoD_w3uFw"]
var test_text = ""
var nextUrl = ""
//func
func refresh(){
tableView.reloadData()
}
#IBAction func plus(_ sender: NSButton) {
if urlInput.stringValue == "" {
} else {
test_text = urlInput.stringValue
test_data.append(test_text)
urlInput.stringValue = ""
tableView.reloadData()
// fonction du bouton +
}
}
#IBAction func nextLien(_ sender: NSButton) {
if test_data == [] {
} else {
nextUrl=test_data[0]
var monUrl = URL(string: nextUrl)
var maRequete = URLRequest(url: monUrl!)
view_web.load(maRequete)
test_data.remove(at: 0)
tableView.reloadData()
//fonction du bouton pour le prochain lien
}
}
func numberOfRows(in tableView: NSTableView) -> Int {
return test_data.count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
return test_data[row]
}
//var
#IBOutlet weak var urlInput: NSTextField!
#IBOutlet weak var view_web: WKWebView!
#IBOutlet weak var tableView: NSTableView!
//func vitale
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string: "https://www.youtube.com")
let myRequest = URLRequest(url: myURL!)
view_web.configuration.preferences.plugInsEnabled = true
view_web.load(myRequest)
// Do any additional setup after loading the view.
}
}
What I don't understand, it's that the viewtable.reloaddata() works fine in the ViewController.swift file, but the same instruction doesn't work when I try to do it in the AppDelegate file.
I do have check that my array of strings (test_data) is not empty. (It contains 4 elements after I load and extract the data of a ".txt" file that I had created myself.)
I would like to know how could I fix this error so the data is shown in my TableView after I parse my txt file.
Thank you very much.
There is a simple solution: Move the IBAction to the view controller.
In Interface Builder disconnect the IBAction from AppDelegate.
Move the openUnFichier method to ViewController (delete it in AppDelegate).
In Interface Builder connect the menu item to First Responder (the red cube) and choose openUnFichier in the list. The first class in the responder chain which implements the method will execute it.

want to create a search bar like google in swift

I have an array like ["apple","appear","Azhar","code","BCom"] etc. This array contain more than half a million of records.
Now what I want to do, is to place a UISearchBar like in google and then whenever user types a text, then the dropdown list would appear with all the results containing this text and user could select one from the list.
For example - if the user types "a", then "apple","appear" and "Azhar" would appear in a drop-down list.
I don't want to use a UITableView or anything else to load the records. Whenever user types any word it should collect records from the array and make a drop down to display them.
How can I do this?
Suggestions required please.
Pretty simple code that would do the trick, the search bar filter is easy, as for the drop down menu i use a third party Pod named 'DropDown' that is very easy to use : https://github.com/AssistoLab/DropDown
import UIKit
import DropDown
class ViewController: UIViewController, UISearchBarDelegate {
var data: [String] = ["apple","appear","Azhar","code","BCom"]
var dataFiltered: [String] = []
var dropButton = DropDown()
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
dataFiltered = data
dropButton.anchorView = searchBar
dropButton.bottomOffset = CGPoint(x: 0, y:(dropButton.anchorView?.plainView.bounds.height)!)
dropButton.backgroundColor = .white
dropButton.direction = .bottom
dropButton.selectionAction = { [unowned self] (index: Int, item: String) in
print("Selected item: \(item) at index: \(index)") //Selected item: code at index: 0
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
dataFiltered = searchText.isEmpty ? data : data.filter({ (dat) -> Bool in
dat.range(of: searchText, options: .caseInsensitive) != nil
})
dropButton.dataSource = dataFiltered
dropButton.show()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchBar.setShowsCancelButton(true, animated: true)
for ob: UIView in ((searchBar.subviews[0] )).subviews {
if let z = ob as? UIButton {
let btn: UIButton = z
btn.setTitleColor(UIColor.white, for: .normal)
}
}
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = false
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
searchBar.text = ""
dataFiltered = data
dropButton.hide()
}
}

How do I check when a NSTextView changes if I have two text views?

In a macOS app with two NSTextView, I am trying to check when a textView changes, so far I have done this:
class ViewController: NSViewController, NSTextViewDelegate {
#IBOutlet var mainTextField: NSTextView!
#IBOutlet var findPanelFindTextView: NSTextView!
func textDidChange(_ notification: Notification) {
print("Hello!") // works only with mainTextField
}
override func viewDidLoad() {
super.viewDidLoad()
mainTextField.delegate = self // for textDidChange
findPanelFindTextField.delegate = self // for textDidChange
}
}
Only the first NSTextView (mainTextField) triggers the method textDidChange.
I already see this question Check if NSTextView has been edited and that implementation works for my first textView but not for my second textView.
Besides the fact that you wrote findPanelFindTextField but it should be findPanelFindTextView, you should check the object that posted the notification, create a textView object from it but cast it from Any to NSTextView and then create a switch to check which textview is posting the notification:
#IBOutlet var mainTextView: NSTextView!
#IBOutlet var findPanelFindTextView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
mainTextView.delegate = self
findPanelFindTextView.delegate = self
}
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else { return }
switch textView {
case mainTextView:
print("mainTextView changed")
case findPanelFindTextView:
print("findPanelFindTextView changed")
default:
break
}
}

Swift how to update the Search result when touch return key

I Swift, i make a Search View using UISearcheController and UISearcheResultUpdating
and i update the result with func updateSearchResultsForSearchController
if self.searchController?.searchBar.text.lengthOfBytesUsingEncoding(NSUTF32StringEncoding) > 0 {
let searchBarText = self.searchController!.searchBar.text
var arrResult = DFManager.GetResult(searchBarText)
self.results?.addObjectsFromArray(arrResult)
// Reload a table with results.
self.searchResultsController?.tableView.reloadData()
}
But that is result always updating when i type char by char, it make this app slowly. I want this result only update when type return key?
Try this:
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
//do something
searchBar.resignFirstResponder() //hide keyboard
}
And dont forget to add the searchBar delegate:
class SomeViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
searchBar.delegate = self
}
}