Xcode with Swift: How can I pass object back and forth in wizard steps? - xcode

I am stuck on implementing a rather simple task.
I have wizard with 3 steps in which you create an entity of course.
The wizard consists of a navigation view followed by 3 regular view.
In the contoller of the first step I create the course object and pass it to next step via the segue event:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "Step1_To_Step2") {
let step2Controller = segue.destinationViewController as Step2Controller
step2Controller.course = self.course
}
}
What I can't figure out though is:
How can I pass the object when going the other way, from step 2 to step 1, for exmaple?
Is it good practice, memory-wise to replace the whole object each time or just specific fields within? (The object may contain an image or two)

Please check some attached images. It may help you to understand delegate if you not already aware of it. Attached first image is for segue and second for delegate.
Segue
Result

Related

How to add a view to NSGroupTouchBarItem

Trying to make a mutable list of buttons that show on the touch bar
override func windowDidLoad() {
super.windowDidLoad()
let customViewItem = NSCustomTouchBarItem(identifier: NSTouchBarItemIdentifier(rawValue: "identifier"))
customViewItem.view = NSTextField(labelWithString: "🌎 📓")
groupTouchBarItem.view?.addSubview(customViewItem)
}
But I get Cannot invoke 'addSubview' with an argument list of type '(NSCustomTouchBarItem)' on the bottom line.
How can I add buttons to the touch bar and remove them at will? Is there an array I can through views into or should I always addSubview on the touchbar view?
I'd recommend checking out this guide on creating NSTouchBar and NSTouchBarItems, specifically "NSTouchBar Objects", "Using NSTouchBarItem Objects"; as well as the documentation for NSGroupTouchBarItem
In short, when you create Touch Bar content you do so by composing NSTouchBarItems into an NSTouchBar, and not their views. (You primarily only deal with views when setting them on NSCustomTouchBarItems) And with NSGroupTouchBarItem, you are adding additional items to its groupTouchBar.
There are some conveniences, e.g.: NSGroupTouchBarItem(identifier: .myGroupItem, items: [customViewItem]), but you can also incrementally add to it with:
groupItem.groupTouchBar.templateItems.insert(customViewItem)
groupItem.groupTouchBar.defaultItemIdentifiers.append(customViewItem.identifier)

xcode 7.3 reference icon from assets folder for prototype table view cell

In Xcode 7 with swift, I'm trying to use icons from my assets folder to display in the icon slot in a normal prototype tableview cell.
My code compiles and the app runs fine, displaying all the proper row text titles in the table, but all the cells use an icon that I (perhaps stupidly) entered into the attributes inspector for the prototype table cell.
By default, the style=Basic table cell does not have an icon to the left of the Title in the storyboard. BUT... if you add the name of an icon image from the assets folder in the image=MyAssetsIconName, the UI builder automatically adds a UIImageView to the left end of the table row cell. And of course all rows display the image that you selected. Which is what my app currently does.
But I would like to change the icon image for each row, so that the image on any row matches the text rows that I display. So I tried to assign a new image (from the assets folder) to each row cell as it was created. I did the assignment right after I assigned the text title to the cell.
Here is my code, which runs fine (but doesn't display the images that I want).
class TsbReportsCell: UITableViewCell {
#IBOutlet weak var Title: UILabel!
#IBOutlet weak var Detail: UILabel!
#IBOutlet weak var Icon: UIImageView!
...
}
In the table view controller:
In the table view controller class:
// define datasources
var reportnames = [String]()
var reportimages = [String]()
override func viewDidLoad() {
super.viewDidLoad()
reportnames = ["Balance Report",
"Routine Report",
"Low Quality Report"]
reportimages = ["enter.png",
"exterior.png",
"export.png"]
}
override func tableView(tableView:.... {
// all this code works fine
let cell = tableView.dequeueReusableCellWithIdentifier...
let row = indexPath.row
cell.Title.text = reportnames[row]
// here are the lines that seem to have no effect at all
let rowimage = UIImage(named: reportimages[row])
cell.Icon = UIImageView(image: rowimage)
return cell
}
Any ideas what I'm doing wrong? I named an image in the attributes for the table cell row just to get the UI builder to add the imageview placeholder for me. But maybe I'm not allowed to ever override that image, even though I can create an outlet for it.
This seems so simple a problem, but I searched at least 20 posts, the net, the doc, and I still couldn't find anything. Thanks
It's very simple. You are saying:
cell.Icon = UIImageView(image: rowimage)
But that does not put this image view into the interface! It merely assigns it to the cell's property.
Instead, you need to have an image view in the cell and hook an outlet from the cell's Icon property to that image view. As a result, cell.Icon is the image view in the interface. Now you say
cell.Icon.image = rowimage
Man, are you ever fast at answering questions, Matt. I'm sure I'm not the only one who appreciates that.
It's funny how taking a half hour to write up a decent question for this site makes you start thinking about other things to try. I took a seg fault on one of my tries, and was poking around in the error messages. From them, I got the idea that an ImageView was a structure that had an "image" field in it (or something like that), so I tried some UIImageView x UIImage crosses to see what would happen.
Sure enough, I was trying to assign an ImageView to an Image (I think; I could still be wrong). Here's the code line that worked for me.
cell.Icon.image = UIImage(named: reportimages[row])
So you can see I'm assigning an image to an image here, which works properly. AND... it must be right, because it's the same code as Matt's!! :-)

Swift 2.0 and Xcode-beta 7.0 – Using UITextField for numeric input

Completely frustrated noob here. Surely this isn't as hard as it looks?
I want to use a number entered by the user, perform a calculation, and send the result back to the screen.
I have code working that can use a string forced in by code, convert it to double, do the math and send the result to the screen. For example:
#IBAction func buttonPressed() {
NSLog("Button Pressed")
let decimalAsString = "123.45"
let decimalAsDouble = NSNumberFormatter().numberFromString(decimalAsString as String)!.doubleValue
TempLabel.text = "\(decimalAsDouble+2.45)"
}
This simply adds 2.45 to the string "123.45" and sends the result 125.9 back as a string to my label for display, all when the button is pressed. Great. This simpler form also works:
let decimalAsDouble = Double(decimalAsString)
What I have been struggling with is using a number entered into the UITextField.
My UITextField uses a decimal pad for entry, and I've always had a number entered there when the errors were thrown. (Or did I? The numbers show on screen but are they really "entered"? Hmmm...)
No matter what I try, I cannot find code that will both compile and then not blow up at execution, when the button is pressed. The error I get generally complains about unwrapping an optional nil.
I can detail some of the things that DON'T work, if that helps.
OK, well I've finally solved my own problem. My problem was with closing the entry field and dismissing the numeric entry pad. Once I did that, the entered value became available.
The critical code, in the view controller, was:
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?){
view.endEditing(true)
super.touchesBegan(touches, withEvent: event)
}
This dismisses the entry pad when the user clicks anywhere in the view except for the field itself or a button. Programming that looks for the entered value doesn't blow up.

Can't setup bindings in Swift Storyboard?

I've got some code that runs a fairly complex algorithm. I want to put together a fairly simple UI that will allow me to monitor the values of the various variables in the algorithm in some graphical ways -- think of it like a dashboard of sorts.
So, for simplicity's sake, let's say I have an algorithm like what follows. It searches a vector of values for the two values that most closely sum to a target value:
import Foundation
class algorithm {
var numbers = [Double]()
let numberOfRandoms = 1000
dynamic var a: String
dynamic var b: String
init () {
// Load initial vector with some random numbers between 0 and 1
for _ in 1...numberOfRandoms {
numbers.append(Double(arc4random()) / Double(UINT32_MAX))
}
a = " "
b = " "
}
func findTheTwoNumbersThatAddUpTheClosestToTarget(target: Double) {
//Initializing this to a very large value
var currentBestSum = 1000.0
//Begin brute force search for the optimal solution
for i in 0...numbers.count-2 {
for j in i+1...numbers.count-1 {
//Check to see if the current candidate exceeds the best solution
if abs(numbers[i] + numbers[j] - target) < currentBestSum {
//If it does, store the new champion
a = String(i)
b = String(j)
//And reset the current top score to match
currentBestSum = abs(numbers[i] + numbers[j]-target)
}
}
}
}
}
Now, this is just a simple (and silly) example, but it suits these purposes. I basically want to create a simple UI that displays the important values in the process as it runs (dynamically).
In this example, let's say that I just want to display two labels that contain the index values of the two leaders as the algorithm executes.
I created the labels in the storyboard.
Then, I created IBOutlets in the ViewController (Actually, storyboards did it for me when I Ctrl-dragged):
class ViewController: NSViewController {
#IBOutlet weak var a: NSTextField!
#IBOutlet weak var b: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Then, I ctrl-dragged the labels to the a and b in the algorithm class to create the bindings.
Finally, I create an class variable in the view controller and instantiate it in the viewDidLoad method. This doesn't seem like the right thing to do -- maybe it is. Seems like you would want to keep separate your interface and data...
The labels do, in fact, show up -- but they never show any values of a and b. They just show the default text.
Not sure what I'm doing wrong.
Help!?
P.S., in response to Anthony Kong, I do recognize that I could manually synchronize all the view elements in the code, but I thought the whole point of using bindings was to avoid having to do this manual synchronization. I just can't figure out how to set it up.
Without commenting on your specific code I think I have experienced (and solved) the problem you describe. I was able to write an app that had two targets, one NIB-based and one Storyboard-based. As much as I was able I duplicated the code in each and shared the common data instance that I was trying to display in a TableView. The NIB-based app worked using the stock Cocoa Bindings that I set in IB. But the Storyboard-based app did not, the array controller did not see the data.
My solution was simply to add the binding for contentArray programmatically in viewDidLoad. The one line that fixed it for me is:
ac.bind("contentArray", toObject: cd, withKeyPath: "people", options: nil)
ac is the IBOutlet for the ArrayController in the Storyboard. cd is the class instance that contains the people array.
This is using XCode 6.2 (6C107a) which is Beta 3 I think.
This was the only binding that I had to set myself, the TableView to ArrayController (arrangedObjects) and TableViewCell to TableView (objectValue) didn't need any tweaking.
There are several problems with your code
1) In your code,
func applicationDidFinishLaunching(aNotification: NSNotification?) {
// Insert code here to initialize your application
var test = algorithm()
test.findTheTwoNumbersThatAddUpTheClosestToTarget(0.5)
}
The variable test goes out of scope when the function exits. Based on the wording of your question, you are expecting it to be a long running process. So this will not do what you want.
You should
a) Add test as class variable to the ViewController class
b) instantiate the variable in viewDidLoad method.
2) In your algorithm it does not actually provide any feedback to the labels. Maybe you think because the class has the ivar a and b so they are hooked to the IBOutlet by the same names. But of course it is not the case. And you do not need the keyword dynamic too.
What you should do is:
a) provide a method in the View Controller class to update the labels. It will serve as a callback function to be used by algorithm class to feedback the calculation result.
It may look like this:
func update_value_callback(vala: String, valb: String) {
a.text = vala; // updating the label here
b.text = valb;
}
b) make the algorithm class calls the callback function e.g.
func findTheTwoNumbersThatAddUpTheClosestToTarget(target: Double, viewController: ViewController) {
// do your stuff
...
// execute the callback
viewController.update_value_callback(a, b)
}

Access data from table view cell in Swift

I'm having trouble accessing an optional string in swift from a table view cell. Each cell has a title label as well as a detail text label and the amount of cells at any given time in the table view is dependent on the user (the table view is used to display saved values). Since the strings saved as the detail text label's text in each cell are way too long to read in the table view, I wish to load them in another view that is segued to when a cell is pressed. This is my prepare for segue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "cellSegue"
{
let cell = tableView.cellForRowAtIndexPath(tableView.indexPathForSelectedRow()!)
var transfer : ExplanationView = segue.destinationViewController as ExplanationView
if let unwrapped = cell?.textLabel?.text!
{
transfer.infoText.text = unwrapped
}
}
}
When a segue is performed, I get a fatal crash, the offending line is "transfer.infoText.text = unwrapped" and Xcode tells me it found nil when unwrapping an optional. During my debuggingg efforts, I've tried "println(cell?.textLabel?.text!)" and Xcode prints "Optional(String)" where "String" is the actual string I'm trying to access so it seems like I'm on the right track, but obviously there's something I'm missing. I've also tried "cell?.textLabel?.text" but I get the same error. Any help is appreciated
You shouldn't ever extract data from a view. You should extract data from a model. The fact that you have a tableView with cells implies you have a model for the data that goes in those cells. It might be an array of strings or whatever. Instead of trying to extract the data from the cells, just get the data from the data model you used to populate the cells. Something like:
let selectedIndexPath = tableView.indexPathForSelectedRow()
let selectedData = data[selectedIndexPath.row]
Your problem is not unwrapped but instead transfer.infoText.text. Your IBOutlets are not set up at the time of the prepareForSegue. You need add a property (var) to your destination view controller to hold the unwrapped value. Then in viewDidLoad when the IBOutlets are set up, copy the string to your text field.

Resources