Expand item in NSOutlineView with NSTreeController - swift

I'm trying (unsuccessfully) to get the node from a disclosure button clicked
I think this function is the more appropriate:
func outlineViewItemDidExpand(_ notification: Notification) {
let nodeToExpand = notification.userInfo as! Node
let nodeToExpand2 = notification.userInfo["NSObject"] as! Node
//Error #selector(_outlineControlClicked:) from sender NSButton 0x10053d710
}

NSTreeController wraps your nodes in NSTreeNode objects. Your node is representedObject of the NSTreeNode.
if let treeNode = notification.userInfo["NSObject"] as? NSTreeNode,
let node = treeNode.representedObject as? Node {
…
}

Related

how to get parent item of selected item outlineview Swift

I have multilevel hierarchy in my outlineview. I am able to get the selected item and it's parent item using below code.
func outlineViewSelectionDidChange(_ notification: Notification) {
guard let outlineView = notification.object as? NSOutlineView else {return}
if let item = outlineView.item(atRow: outlineView.selectedRow) as? CubeData{
// Getting the parent item
if let parentItem = outlineView.parent(forItem: item) as? CubeData{
print("Parent Item value:", parentItem.node)
}
print("Selected Item value:", item.node)
}
}
But not able to get all the grand parent items.
For example A->B->C->D is my hierarchy and whenever I am selecting "D" I am able to get "C" and "D". I want all the parent items. Any help?

How to get Custom Cell to update labels to information from core data

I'm not getting any errors, just not the result I am looking for.
Here is where I gather data from the user
(If this is not relevant please let me know so I can declutter the post)
I want my custom cell to display the core data record once it's added, but it keeps displaying placeholder labels instead of updating.
class ViewController: UIViewController,UITableViewDataSource, UITableViewDelegate
{
var parties: [NSManagedObject] = []
#IBOutlet weak var tableView: UITableView!
#IBAction func addParty(_ sender: UIBarButtonItem)
{
/*init alert controller with title, message & .alert style*/
let alert = UIAlertController(title: "New Name",
message: "Add a new name",
preferredStyle: .alert)
/*create a name text field, with placeholder "name"*/
alert.addTextField(configurationHandler: { (textFieldName) in
textFieldName.placeholder = "name"
})
/*create a ssn text field, with placeholder "ssn"*/
alert.addTextField(configurationHandler: { (textFieldSize) in
textFieldSize.placeholder = "size"
})
/*create a ssn text field, with placeholder "ssn"*/
alert.addTextField(configurationHandler: { (textFieldContact) in
textFieldContact.placeholder = "contact"
})
/*create a ssn text field, with placeholder "ssn"*/
alert.addTextField(configurationHandler: { (textFieldLocation) in
textFieldLocation.placeholder = "location"
})
/*create a save action*/
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
/*find textfield's text (name) guard let way to get unwrap value otherwise return early*/
guard let textField = alert.textFields?.first,
let nameToSave = textField.text else {
return
}
/*find textfield's text (ssn) guard let way to get unwrap value otherwise return early*/
guard let textFieldSize = alert.textFields?[1],
let sizeToSave = textFieldSize.text else {
return
}
/*find textfield's text (ssn) guard let way to get unwrap value otherwise return early*/
guard let textFieldContact = alert.textFields?[2],
let contactToSave = textFieldContact.text else {
return
}
/*find textfield's text (ssn) guard let way to get unwrap value otherwise return early*/
guard let textFieldLocation = alert.textFields?[3],
let locationToSave = textFieldLocation.text else {
return
}
/*call save method by passing nameToSave and SSNToSave*/
self.save(name: nameToSave, size: sizeToSave, contact: contactToSave, location: locationToSave)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel",
style: .default)
alert.addAction(saveAction)
alert.addAction(cancelAction)
present(alert, animated: true)
}
// Save core data function
func save(name: String, size : String, contact: String, location: String)
{
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
/*1.
Before you can save or retrieve anything from your Core Data store, you first need to get your hands on an NSManagedObjectContext. You can consider a managed object context as an in-memory “scratchpad” for working with managed objects.
Think of saving a new managed object to Core Data as a two-step process: first, you insert a new managed object into a managed object context; then, after you’re happy with your shiny new managed object, you “commit” the changes in your managed object context to save it to disk.
Xcode has already generated a managed object context as part of the new project’s template. Remember, this only happens if you check the Use Core Data checkbox at the beginning. This default managed object context lives as a property of the NSPersistentContainer in the application delegate. To access it, you first get a reference to the app delegate.
*/
let managedContext = appDelegate.persistentContainer.viewContext
/*
An NSEntityDescription object is associated with a specific class instance
Class
NSEntityDescription
A description of an entity in Core Data.
Retrieving an Entity with a Given Name here person
*/
let entity = NSEntityDescription.entity(forEntityName: "Party",
in: managedContext)!
/*
Initializes a managed object and inserts it into the specified managed object context.
init(entity: NSEntityDescription,
insertInto context: NSManagedObjectContext?)
*/
let party = NSManagedObject(entity: entity,
insertInto: managedContext)
/*
With an NSManagedObject in hand, you set the name attribute using key-value coding. You must spell the KVC key (name in this case) exactly as it appears in your Data Model
*/
party.setValue(name, forKeyPath: "name")
party.setValue(size, forKeyPath: "size")
party.setValue(contact, forKeyPath: "contact")
party.setValue(location, forKeyPath: "location")
/*
You commit your changes to person and save to disk by calling save on the managed object context. Note save can throw an error, which is why you call it using the try keyword within a do-catch block. Finally, insert the new managed object into the people array so it shows up when the table view reloads.
*/
do {
try managedContext.save()
parties.append(party)
tableView.reloadData()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
// TABLE VIEW CODE
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int
{
return parties.count
}
//NEED TO FIX WHY CUSTOM CELL NOT DISPLAYING INFO
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
(print(tableView.dequeueReusableCell(withIdentifier: "PartyCell", for: indexPath)))
let party = parties[indexPath.row] as NSManagedObject
let cell = tableView.dequeueReusableCell(withIdentifier: "PartyCell",
for: indexPath) as! PartyCell
cell.nameLabel?.text = party.value(forKeyPath: "name") as? String
cell.sizeLabel.text = party.value(forKeyPath: "size") as? String
cell.contactLabel.text = party.value(forKeyPath: "contact") as? String
cell.locationLabel.text = party.value(forKeyPath: "location") as? String
return cell
}
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Get rootNode of node

In an ARKit project, when the user taps the screen, I want to get the rootNode of the element, that the user want to interact with.
Gesture & Hit test
func screenTapped(_ sender: UITapGestureRecognizer) {
let hitTestResult = sceneView.hitTest(touchLocation)
if let result = hitTestResult.first {
guard let rootNode = getRoot(for: result.node) else {return}
...
}
Recursive function to get the root node
func getRoot(for node: SCNNode) -> SCNNode? {
if let node = node.parent {
return getRoot(for: node)
}
else {
return node
}
}
But it seems odd to me that Swift doesn't offer something by default, while offering recursive methods for child nodes.
Is there an alternative/better approach to this?
Should I write this function as extension for SCNNode?
Isn't it equivalent to sceneView.scene.rootNode?

Function creating 2 identical instances of class instead of only 1

I am relatively new to iOS development, any help will be greatly appreciated!
I'm trying to create a new instance of a class 'Event'.
class Event {
var EventName: String
var EventPhoto: UIImage?
init?(EventName: String, EventPhoto: UIImage?) {
guard !EventName.isEmpty else {
return nil
}
// Initial initilization of the values
self.EventName = EventName
self.EventPhoto = EventPhoto
// If some of the values are left blank, this will return nil to signal the problem
}
}
Below is the override function, which from my understanding is responsible for creating the instance:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard let button = sender as? UIBarButtonItem, button === saveButton else {
os_log("Cancelling Action, The Save Button Was not Pressed", log: OSLog.default,type: .debug)
return
}
let EventName = NewEventNameField.text ?? ""
let EventPhoto = NewEventImage.image
event = Event(EventName: EventName, EventPhoto: EventPhoto)
}
From my understanding, the override function should create a new instance of the class, which would then be displayed in a table view controller displaying a table of 'events'. My problem here is; when the function is called by the "create instance" button, it creates 2 identical instances with the same EventName and EventPhoto extracted from a textfield and an image in the view controller. In the tableview, there are basically 2 events that are exactly the same being displayed, which is what I am having trouble with since I don't see the code calling init twice anywhere, and can't figure out why the instances was created twice. After being created the 2 instances act independently and function like 2 separate instances would.
Thanks!
Thank You for the help, I found the issue in TableViewController's code file:
#IBAction func unwindToEventList(sender: UIStoryboardSegue){
if let sourceViewController = sender.source as?
NewEventViewController, let event = sourceViewController.event{
if let selectedIndexPath = tableView.indexPathForSelectedRow {
events[selectedIndexPath.row] = event
tableView.reloadRows(at: [selectedIndexPath], with: .none)
} else {
//Adding a new event instead of editing it.
let newIndexPath = IndexPath(row: events.count, section: 0)
events.append(event)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
//let newIndexPath = IndexPath(row: events.count, section: 0)
//events.append(event)
//tableView.insertRows(at: [newIndexPath], with: .automatic)
}
}
Turns out that I accidentally appended the instance to the list events and inserted it into the table an extra time outside the if statement.

Storing Button Pressed In Swift

I have collection view where you can select 4 buttons, it is like a quiz with A, B, C, D. I need to store which one they clicked before they go to the next question (They will swipe to go to the next question since it is a collection view) The controller looks like this:
First: Essentially the code used to display the image above, I have created a database which is parsed using this:
struct Question {
let fact: String
let question: String
let answers: [String: String]
let correctAnswer: String
let revenue: String
init?(with dictionary: [String: Any]) {
guard let fact = dictionary["fact"] as? String,
let question = dictionary["question"] as? String,
let answerA = dictionary["answer_a"] as? String,
let answerB = dictionary["answer_b"] as? String,
let answerC = dictionary["answer_c"] as? String,
let answerD = dictionary["answer_d"] as? String,
let revenue = dictionary["revenue"] as? String,
let correctAnswer = dictionary["correctAnswer"] as? String else { return nil }
self.fact = fact
self.question = question
self.revenue = revenue
var answersDict = [String: String]()
answersDict["answer_a"] = answerA
answersDict["answer_b"] = answerB
answersDict["answer_c"] = answerC
answersDict["answer_d"] = answerD
self.answers = answersDict
self.correctAnswer = correctAnswer
}
Second: Then I display using this code:
extension QuestionCell {
func configure(with model: Question) {
factLabel.text = model.fact
questionLabel.text = model.question
revenueLabel.text = model.revenue
let views = answersStack.arrangedSubviews
for view in views {
view.removeFromSuperview()
}
for (id, answer) in model.answers {
print(index)
print(id)
let answerLabel = UILabel()
answerLabel.text = answer
answersStack.addArrangedSubview(answerLabel)
let answerButton = UIButton()
let imageNormal = UIImage(named: "circle_empty")
answerButton.setImage(imageNormal, for: .normal)
let imageSelected = UIImage(named: "circle_filled")
answerButton.setImage(imageSelected, for: .selected)
answerButton.setTitleColor(.black, for: .normal)
answerButton.addTarget(self, action: #selector(answerPressed(_:)), for: .touchUpInside)
answersStack.addArrangedSubview(answerButton)
}
}
}
Is there a way to store the button I clicked? Thanks
Well there is one sure shot way out of this situation.
Make a custom cell for the collectionView.
Add button outlets and action the the customCell's class
Create and make use of delegate methods in customCell's class and when implementing the customCell in your ViewController set the delegate to
self.
Trigger the delegate methods when button actions are done (inside your custom cell).
Provide your customCell the current indexpath when using it in cellForItemAt method.
Make use of that indexPath to decide which button was triggered.
You should be thinking something close to this approach for a robust solution.
The way I've handled this in the past is to use the tag on a UIButton and just keep track of which tag is currently selected. With this approach I can use the same IBAction for every button and all I need to do is pull the tag from the sender in the function body. While maybe not as flexible and robust is an approach using subclassing, it's a bit quicker to implement.
First set your tags when you create your buttons (I use 100-104 to avoid conflicts with other buttons). Since you're creating your buttons in a CollectionView, you'll need to set the tag in your configure() function:
func configure(with model: Question) {
...
for (id, answer) in model.answers {
...
answerButton.setImage(imageSelected, for: .selected)
answerButton.tag = index
answerButton.setTitleColor(.black, for: .normal)
answerButton.addTarget(self, action: #selector(answerPressed(_:)), for: .touchUpInside)
}
}
Create an instance variable:
var selectedAnswerIndex = -1
Then assign this IBAction to each of you buttons:
func answerPressed(_ sender: UIButton){
selectedAnswerIndex = sender.tag
hilightNewOne(sender: sender)
}