Renaming an outlet in Swift - ios

I accidentally misspelled one of the outlet's in my view controller and ran into a few issues. When I manually try to correct the typo I get stopped at runtime within AppDelegate I'm shown the message,
Thread 1: signal SIGABRT
which highlights the beginning of the code block:
class AppDelegate: UIResponder, UIApplicationDelegate {
...
}
I've found that to fix this issue in Objective-C you would right-click the outlet's original name and "Refactor => Rename" but unfortunately I get the message:
Xcode can only refactor C and Objective-C code
View Controller
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var billTextField: UITextField!
#IBOutlet weak var tipRateSegmentedControl: UISegmentedControl!
#IBOutlet weak var tiLabel: UILabel! // Variable name should be "tipLabel"
#IBAction func calculateTapped(sender: AnyObject) {
var userInput = billTextField.text as NSString
var totalBill: Float = userInput.floatValue
var index: Int = tipRateSegmentedControl.selectedSegmentIndex
var tipRate: Float = 0.15
if index == 0 {
tipRate == 0.15
} else if index == 1 {
tipRate = 0.20
} else {
tipRate = 0.25
}
var tip: Float = totalBill * tipRate
tipLabel.text = "$\(tip)"
}
}

EDIT: Since Xcode9, there's a refactor function; the new good method is the one in this answer
Since Xcode 6, you can look for your outlet names directly in the Find navigator (Cmd+3), it shows occurrences in your code and in your xibs and storyboards. It also works with actions.
Just search the name in the Find navigator.
You can see that the second result is a reference in a storyboard. You can replace one by one by clicking on each result and press Replace, or you can directly Replace All if you are sure you don't break anything.

Refactor > Rename...
Xcode 9 had many editor improvements including a feature that changes an outlet/action name in the code and storyboard from one place.
In the example Swift code below, right click on btnRequestCode, from the popup menu select "Refactor > Rename...", change the outlet/action name and Xcode changes it in the Storyboard also.
#IBOutlet weak var btnRequestCode: UIButton!

With swift this kind of refactor doesn't work yet. You have to change the name of the outlet, and then delete and reset the connection in the interface builder.
Updated:
Right click the IBOutlet that you want to rename, delete reference outlet, and then reassign it, but you don't have to do anything with your code.

I like to fix situations like this by just opening the Storyboard as source code and editing the XML-like tags.
Right click the .storyboard file and choose "Open As". Search for the old outlet (or action/method) name that is still set in the storyboard but which no longer matches the renamed method/outlet object in the View Controller. Edit the Xml tag in the storyboard to give it the new name of your outlet or action in the view controller.

Related

Where should I drag and drop "label"?

Now I'm learning swift using Xcode, but I don't know where to drag and drop and why drag and drop "label" to somewhere
should I drop no.1? or no.2? or no.3? and why I should drop there?
If you drop it to 1, Xcode automatically generate an #IBOutlet for you. If you drop it to 2 or 3, then #IBAction. There's no other reason for that except that Xcode tries to be smart and help you organize code more nicely: properties with properties in the top of the class, methods – in the methods area. And you can also move declarations to another place later: except for code style matters, it doesn't matter, where exactly within your class you put declarations.
as you are trying to take an IBOutlet for a label , its nice to keep it on top of the class (where you mention 1).
normally Xcode gives suggestion you , if you drug on top side it will be #IBOutlet
or you on bottom like 2 or 3 it will be #IBAction like you already took a button action in your code .
Your Solution On Code:
import UIKit
class CodePresentViewController: UIViewController {
#IBOutlet weak var YourLabel: UILabel! // insert #IBOutlet type property here
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func tapBackButton(_ sender: UIButton) {
//Yout Button COde
}
}
If you want to own the label in your code, and then make some configuration to the label by code, use "1". It will give you a #IBOutlet label object in your code.
If you want to set the label's action, use "2" or "3".

this class is not key value coding-compliant error? Xcode 11

Currently, I've been following a Coursera course called Intro to Swift Programming 5. The video I was following was about the Xcode's MVC, and I'm getting the following error, even though I did what the person on screen had done:
Thread 1: Exception: "[< UIViewController 0x7fd2b6c053d0>
setValue:forUndefinedKey:]: this class is not key value
coding-compliant for the key colorLabel."
I've tried looking it up, but I don't see where I would have referenced something and deleted it. I've also checked to see if there are any warning signs near any of my reference outlets on the Main Storyboard, but everything seems to be in order there as well.
My code is as follows:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var modelLabel: UILabel!
#IBOutlet weak var colorLabel: UILabel!
#IBOutlet weak var priceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let appleProduct = AppleProduct(name: "iPhone X", color: "Space Gray", price: 999.99)
modelLabel.text = appleProduct.name
colorLabel.text = "in \(appleProduct.color)"
priceLabel.text = "$\(appleProduct.price)"
}
}
Has anyone else experienced this issue and can help me? I'd greatly appreciate it, and I'm happy to provide any other code that's needed if this is unclear. Thanks!
CustomPrettyView is the class of the view. But you also need to declare the class of the view controller itself; otherwise Xcode will decide that it's UIViewController.
Click on the scene and in the inspector in the "Custom Class" section it will say that the class is UIViewController and it will be faded because you haven't overridden it. Change that to be the name of your view controller, which is ViewController.
I would also recommend that you change that class name to be something more meaningful, maybe PrettyViewController or something like that.

App fails to replace label text with UUID

I am trying to work on a program that will change a label to a user's UUID but for some reason it does not work and I can't seem to figure out since I'm still learning the language. Below is my attempt. This is the ViewController.swift file:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var text: UILabel!
#IBAction func getuuid(sender: AnyObject) {
text.text = UIDevice.currentDevice().identifierForVendor!.UUIDString
}
}
Place a breakpoint on the line where you set the text.text value to the UUID. Odds are, you are missing the connection from your storyboard to the function call. To test this, click on the button once the breakpoint is in place, and if you see it freeze the code at that spot, then your connection is at least working. If it does not freeze there, then you need to connect the getuuid function to your button.

Changing text of Swift UILabel

I am attempting to learn Apple's Swift. I was recently trying to build a GUI app, but I have a question:
How do I interact with GUI elements of my app? For instance, I used interface builder to make a UILabel, and I connected it to my viewcontroller by control-clicking, so that I get the #IBOUTLET thing. Now, how do I, while in my view controller, edit the text of this UILabel? To state it another way, what code can I use to programatically control the text of something on my storyboard? All methods I have found online only appear to work with a button generated in code, not a button generated on a storyboard.
I've seen code like
self.simpleLabel.text = "message"
If this is right, how do I link it with the label in question? In other words, how do I adapt this code to be connected with the IBOutlet (If that's what I do)
If you've successfully linked the control with an IBOutlet on your UIViewController class, then the property is added to it and you would simply replace simpleLabel with whatever you named your IBOutlet connection to be like so:
class MyViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
func someFunction() {
self.myLabel.text = "text"
}
}
The outlet you created must've been named by you. The outlet belongs to your view controller. self.simpleLabel means 'fetch the value of my property named 'simpleLabel'.
Since different outlets have different names, using self.simpleLabel here won't work until your outlet is named 'simpleLabel'. Try replacing 'simpleLabel' with the name you gave to the outlet when you created it.
The correct way now would be:
self.yourLabelName.text = "message"
If you have something like this for an IBOutlet:
#IBOutlet var someLabel: UILabel!
then you could set the text property just like in your example:
someLabel.text = "Whatever text"
If you're having problems with this, perhaps you're not assigning the text property in the right place. If it's in a function that doesn't get called, that line won't execute, and the text property won't change. Try overriding the viewDidLoad function, and put the line in there, like this:
override func viewDidLoad() {
super.viewDidLoad()
someLabel.text = "Whatever text"
}
Then, as soon as the view loads, you'll set the text property. If you're not sure if a line of code is executing or not, you can always put a breakpoint there, or add some output. Something like
println("Checkpoint")
inside a block of code you're unsure about could really help you see when and if it runs.
Hope this helps.
You may trying to change a UI component not in the main thread, in that case, do this:
DispatchQueue.main.async {
someLabel.text = "Whatever text"
}

Swift put multiple IBOutlets in an Array

I made these (marked with red border) IBOutlets using ctrl + drag
But i don't like to have the exact same line 9 times (DRY)
How do i put these IBOutlets in an Array?
you can define a generic outlet collection in Swift like this:
#IBOutlet var collectionOfViews: Array<UIView>? // = [UIView]?
or for e.g. UIButton objects:
#IBOutlet var collectionOfButtons: Array<UIButton>? // = [UIButton]?
you can find your collections under the Outlet Collections group as usually are in the File's Owner:
it would look on my console after connecting 5 random buttons:
Follow these steps to create an array of outlets an connect it with IB Elements:
Create an array of IBOutlets
Add multiple UIElements (Views) in your Storyboard ViewController interface
Select ViewController (In storyboard) and open connection inspector
There is option 'Outlet Collections' in connection inspector (You will see an array of outlets there)
Connect if with your interface elements
-
class ViewController2: UIViewController {
#IBOutlet var collection:[UIView]!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Solution here Swift - IBOutletCollection equivalent
#IBOutlet var objectCollection: [Object]
This is for macOS (should be similar for iOS) and I do not find an "Outlet Collections" in my storyboard (looks like they took that option out). So I put all my buttons in an NSStackView and linked the stack from storyboard
#IBOutlet weak var buttons: NSStackView!
and then I looped over them to make changes accordingly
for case let (index, button as NSButton) in buttons.arrangedSubviews.enumerated() {
if(index + 1 != someButtonIndex) {button.state = .off}
else {button.state = .on}
}
you can also use tag instead of index
Start with the two view pane where you see both your code and the storyboard. When you make your first IBOutlet connection from the UI to your code, just look carefully at the Connection drop down field and select the option called "Outlet Collection". This will automatically create an array of IBOutlets. Next just look for the little black circle within a circle that is placed in your code where the array is created. Just drag from this circle to all the other UI objects you want to connect to that same collection (not sure if you can mix types). Similarly you can connect all the objects to one Action by dragging from the first black dot created to all the other objects you want to wire up to that action. Also consider EnumerateSequence() to help in working with this Collection. Sweet right?

Resources