Getting nil value on textview and button - swift

Hello im am making an app where the user can press a button and a random link shows upp. I have in the textfile tried to name blblbl.delegate = self but when running the code it suddenly says that all of my textview and buttons has the value of nil.
class ViewController: UIViewController, UITextViewDelegate,
NSObjectProtocol {
#IBOutlet weak var firstbutton: UIButton!
#IBOutlet weak var firsttextview: UITextView!
let links = ["http://www.google.com","http://www.yahoo.com",
"http://www.discovery.com"]
var currentLinkIndex : Int = 0
override func viewDidLoad() {
Firsttextview.delegate = self
configureTextView()
super.viewDidLoad()
}
func configureTextView() {
func textView(firsttextview: UITextView!, shouldInteractWithURL
URL:
NSURL, inRange characterRange: NSRange) -> Bool {
let url = NSURL(string:links[currentLinkIndex])
UIApplication.sharedApplication().openURL(url!)
return false
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func firstaction(sender: UIButton) {
let random : Int = Int(arc4random()) % 3
Firsttextview.text = links[random]
currentLinkIndex = random
}
}
How do a solve this problem? Does i some how have to give the textview and button a value! Thanks in advance!

I guess you need to change :
Firsttextview.delegate to firsttextview.delegate and
Firsttextview.text = links[random] to firsttextview.text = links[random]
I tried to recreate your example by :
Creating a button in the storyboard
Creating the textview in the storyboard
Create + drag and drop to my ViewController Class the IBOutlet firstbutton from the button in the storyboard
Create + drag and drop to my ViewController Class the IBOutlet firsttextview from the textview in the storyboard
Create + drag and drop to my ViewController Class the IBAction firstaction from the button in the storyboard
If the button and the label are created in the storyboard, they don't need to be initialized in your code.
By doing this, your code worked for me.

Related

How do I present ViewController programatically?

import UIKit
//EventList ViewController
class EventPage: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
class EventForm: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//IBOutlets
#IBOutlet weak var eventNameField: UITextField!
#IBOutlet weak var fromTimePicker: UIDatePicker!
#IBOutlet weak var toTimePicker: UIDatePicker!
#IBOutlet weak var colorPreview: UIView!
#IBAction func cancel(_ sender: Any) {
//empty text field
eventNameField.text = ""
}
#IBAction func save(_ sender: Any) {
if (eventNameField.hasText) {
//fix error handling
eventNameField.backgroundColor = UIColor.systemGray2
//pull data from fields
let text = eventNameField.text!
let fromTime = fromTimePicker.date
let toTime = toTimePicker.date
//initialize object
let currentEvent = EventModel(eventName: text, fromTime: fromTime, toTime: toTime, color: storedColor)
//append to data model
EventDataArray.append(currentEvent)
//transition
present(EventPage(), animated:true)
}
else {
eventNameField.backgroundColor = UIColor.systemRed
}
}
}
I currently have an EventPage class declared as type UIViewController, but upon pressing the save button with a populated text field a transition to a blank ViewController occurs. I've attached the class to the correct ViewController in main.storyboard.
The problem in here is that you are creating a new EventPage but it doesn't inherit from Storyboard.
1
Go to the inspector in your storyboard, select your View Controller, and write an identifier for your View Controller (can be anything)
Write it in Storyboard ID:
2
Replace
present(EventPage(), animated:true)
With
(don't forget to replace 'MYIDENTIFIER' with the id you entered earlier)
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MYIDENTIFIER") as! EventPage
// If you need to do any configurations to your view controller, do that in here.
// For example:
// viewController.label.text = "Hello, world!"
present(viewController, animated:true)
Note
If the name of your Storyboard file name is not called Main, replace "Main" in step 2 with the name of your storyboard file (excluding .storyboard)

Change cell title in TableViewController in another ViewController

I'm trying to change the title in a TableViewController from another ViewController. (see image)
The second ViewController is the one with the 3 cells and the third one is the one with a textfield (inputText in code), a button (changeText) and a label (outputLabel). I would like this app to remember what I put in the text field when I go back to the table view and then back into the ViewController. What happens now is:
- I change the text, hit the button and the label changes.
- I go back to the TableViewController and then I go into the ViewController that I was just in with a changed label
- The label is what it was before...
How can I make the app 'remember' what I put in in the text field and what the label was like? My code (ViewController.swift, I linked the 3rd controller to this file, haven't linked the 2nd controller to anything (yet?)):
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var outputLabel: UILabel!
#IBOutlet weak var inputText: UITextField!
#IBAction func changeText(_ sender: UIButton) {
outputLabel.text = inputText.text
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Thanks in advance!
You can reference your ViewController in first controller (TableViewController),
make public inputText
#IBOutlet public weak var inputText: UITextField!
and in viewDidAppear get your text
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let text = ViewControllerVar.inputText.text //your text
}

unexpectedly found nil while unwrapping an Optional value prepareForSegue

I am beginner in swift and working on one project where I am using collectionView. From collectionView, I want to transfer some values to details view but I am getting the above mentioned error. Values are not nil but somehow, it is giving this error while performing segue. Anybody help me, I am badly stuck here.
//In my CollectionView Controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "DetailsView")
{
if let vc:DetailsViewController = segue.destinationViewController as? DetailsViewController
{
vc.details.text = self.description
vc.line.text = self.subText
vc.startTime.text = self.formatted_time
}
}
}
//DetailsViewController
import UIKit
class DetailsViewController: UIViewController {
#IBOutlet var startTime: UILabel!
#IBOutlet var line: UILabel!
#IBOutlet var details: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
This basically means your IBOutlets are not yet initialised.
You should set strings and then in the viewDidLoad set you labels.
So to sum up:
Add string properties in your DetailsViewController
Set these string properties in your preparForsegue function
in the viewDidLoad of your DetailsViewController, set your labels
Your code should look like something like this :
//In my CollectionView Controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "DetailsView")
{
if let vc:DetailsViewController = segue.destinationViewController as? DetailsViewController
{
vc.detailsString = self.description
vc.lineString = self.subText
vc.startTimeString = self.formatted_time
}
}
}
//DetailsViewController
import UIKit
class DetailsViewController: UIViewController {
#IBOutlet var startTime: UILabel!
#IBOutlet var line: UILabel!
#IBOutlet var details: UILabel!
var startTimeString: String?
var lineString: String?
var detailsString: String?
override func viewDidLoad() {
super.viewDidLoad()
startTime.text = tmpStartTimeString
line.text = tmpLineString
details.text = tmpDetailsString
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Double check my code, I've wrote it very quickly ;)
The IBOutlets in a destination view controller aren't set until some time after prepareForSegue finishes
In DetailsViewController, add three instance variables:
var startTime: String?
var line: String?
var details: String?
Then in prepareForSegue, set those three values:
vc.details = self.description
vc.line = self.subText
vc.startTime = self.formatted_time
Then in viewDidLoad of DetailsViewController
detailsLabel.text = self.details
lineLabel.text = self.line
startTimeLabel.text = self.startTime
As the two answer above are point out when you create an instance of a UIViewController like for example in the prepareForSegue when you call segue.destinationViewController as? DetailsViewController this not mean that the #IBOutlet's are injected or initialized yet. The #IBOutlet's are initialized when the view is fully loaded, so you can do two of the following options:
Create variables in your UIViewController in which you can save the values after the init of the UIViewController in the prepareForSegue and then in the viewDidLoad() of the another UIViewController you set the values for the #IBOutlet's.
Another option is call the view (e.g let _ = vc.view) property when you create the instance of the UIViewController, in this way you can force the view to load load fully and you can set your #IBOutlet's from the prepareForSegue.
I hope this help you.

How would I go about adding a uiview vertically above a uiview within an inputAccessoryView?

I'm trying to add a uiview containing multiple buttons, above my current input accessory view. My current input accessory view is a growing textField (like iOS standard Text Message app).
class ViewController: UIViewController {
let textInputBar = ALTextInputBar()
// The magic sauce
// This is how we attach the input bar to the keyboard
override var inputAccessoryView: UIView? {
get {
return textInputBar
}
}
// Another ingredient in the magic sauce
override func canBecomeFirstResponder() -> Bool {
return true
}
}
Example of what I'm trying to do, the app (Facebook Messenger) has a growing textfield or textinput, and in this case, an bar of buttons bellow.
My current view, as mentioned earlier.
try this code
class ViewController: UIViewController {
#IBOutlet weak var myTextField: UITextField!
var textInputBar:UIView = ALTextInputBar()// if it is a uiview type
override func viewDidLoad() {
super.viewDidLoad()
myTextField.inputAccessoryView = textInputBar
}
}

Swift Text Label Nil Even With Default Value

This is driving me crazy. The function updateTextView() is being called, verified by the print statements, but it is not setting the label in my view controller, and the print statements for the label are returning nil even though it has a default value set which is visible when the app is loaded. Whats more perplexing is that I set up a test button to call this function separately, and when I call it with test(), then the label updates properly.
class GoalDetailViewController: UIViewController, TextDelegate {
#IBAction func test(sender: AnyObject) {
updateTextView()
}
func updateTextView() {
print(goalSummaryTextBox?.text)
print("delegate called")
self.goalSummaryTextBox?.text = GoalsData.summaryText
print(goalSummaryTextBox?.text)
}
#IBOutlet weak var goalTitle: UILabel?
#IBOutlet weak var goalCreationDate: UILabel?
#IBOutlet weak var goalSummaryTextBox: UITextView?
override func viewDidLoad() {
super.viewDidLoad()
goalSummaryTextBox?.text = GoalsData.summaryText
}
}
updateTextView() is being called through a delegate method after I pop a different view controller, as can be seen below:
class TextEditViewController: UIViewController {
var textDelegate: TextDelegate?
#IBOutlet weak var textView: UITextView?
func configureView() {
navigationItem.title = "Edit Description"
navigationItem.setRightBarButtonItem((UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "segue")), animated: true)
}
func segue() {
textDelegate = GoalDetailViewController()
if let text = textView?.text {
GoalsData.summaryText = text
}
textDelegate?.updateTextView()
self.navigationController?.popViewControllerAnimated(true)
}
}
The line causing the issue is in the TextEditViewController below:
textDelegate = GoalDetailViewController()
What this line does is creates a new instance of GoalDetailViewController, and sets it as the delegate to the TextEditViewController. But, what you want is the original instance of GoalDetailViewController to be the delegate. This is why you were seeing the logs when popping TextEditViewController, since it was executing the other instance (which wasn't part of the view hierarchy). It also explains why all your IBOutlets are nil when stepping through updateTextView() on the delegate call, and that the button you added updates the text properly.
The solution is to make the delegate connection in the prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? TextEditViewController {
destination.textDelegate = self
}
}
Add the above code to your GoalDetailViewController.
EDIT:
The below code will ensure that this problem does not happen in the future. Change the delegate's definition to:
weak var textDelegate: TextDelegate?
and change your protocol to:
protocol TextDelegate: class {
func updateTextView()
}