I want to be able to update my uitextview by calling a method within my view. With this code I get a runtime error:
Unexpectedly found nil while unwrapping an Optional value
If I comment out the vw.updateTerm(...) line it runs. Ultimately, I want to update a uitextview with data updated from BLE and/or http request. Any help or direction would be most appreciated.
My playground swift 3 code looks like this:
import UIKit
import PlaygroundSupport
public class TextViewController : UIViewController {
public var textView : UITextView!
override public func loadView() {
textView = UITextView()
textView.text = "Hello World!\nHello Playground!"
self.view = textView
}
func updateTerm(textToUpdate: String) {
self.textView.text.append(textToUpdate)
}
}
let vw = TextViewController()
vw.updateTerm(textToUpdate: "here you go")
PlaygroundPage.current.liveView = vw
The problem is that loadView is still not called at the moment when you are trying to updateTerm, which means that textView is nil in the loadView for the vw instance.
As far as textView is declared as a forced-unwrapped property, any attempt to use it while it is nil (as in your case) will cause a runtime error.
You should make it either a regular optional:
public var textView : UITextView?
override public func loadView() {
textView = UITextView()
textView?.text = "Hello World!\nHello Playground!"
self.view = textView
}
func updateTerm(textToUpdate: String) {
self.textView?.text.append(textToUpdate)
}
or, for example, a lazy property (if suitable in your case):
lazy public var textView = UITextView()
override public func loadView() {
textView.text = "Hello World!\nHello Playground!"
self.view = textView
}
func updateTerm(textToUpdate: String) {
self.textView.text.append(textToUpdate)
}
More info can be found here.
Related
Okay, this might be one of the most basic questions ever, but all answers I find use storyboard to declare an outlet for a label, textfield or whatever element that needs to be changed. I, however, don't use storyboards and write everything in code. Now I have a function setupViews, where I define a textfield:
let usernameInput = UITextField()
Now, I can perfectly set the text or placeholder or whatever inside this setupViews() class, but how can I access it outside? For example, if I have a function logIn(), I want to call usernameInput.text and use it in this function.
Someone who can point me in the right direction? Do I need to declare this textfield globally, in another file, or something else?
When I create my views in code I always associate a property with the view that has all those various display values.
I have not tested this code to see but hopefully the following will give you an idea.
import UIKit
struct {
var name: String
}
class CustomViewController : UIViewController {
// some struct which contains data for view
var customViewData : ViewDataInfo? {
didSet {
labelOnScreen.text = customViewData.name
}
}
var labelOnScreen: UILabel = {
let label = UILabel()
label.text = "Placeholder information..."
// stuff auto layout
label.translateAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private func setupView() {
view.addSubview(label)
// set your constraints here
}
}
say I have this extension that helps me in changing the font size in all text elements of a UIViewController
extension UIView {
func changeFontSize(){
let fontSize = CGFloat(5)
if let v = self as? UIButton {
v.titleLabel?.font = v.titleLabel?.font.withSize(fontSize)
print("didChangeFontSizeFor_Button")
} else if let v = self as? UILabel {
v.font = v.font.withSize(fontSize)
} else if let v = self as? UITextField {
v.font = v.font?.withSize(fontSize)
} else {
for v in subviews {
v.changeFontSize()
}
}
}
}
it works fine when I call it like this
override func viewDidLayoutSubviews() {
view.changeFontSize()
}
now the question, is there a way where I can make this more dynamic to be forced in all viewControllers?
say we have 3 view Controllers, and I want to make some CocoaPods library where people just make a simple call like this
forceAppFontSize.fontSize = CGFloat(15)
to change the font size for all other screens..
class 1
import UIKit
class v1: UIViewController{
override func viewDidLayoutSubviews() {
print("someCommand1")
print("someCommand2")
}
}
class 2
import UIKit
class v2: UIViewController{
override func viewDidLayoutSubviews() {
print("someCommand")
}
}
class 3
import UIKit
class v3: UIViewController{
}
is there a way to make this dynamic without breaking the original viewDidLayoutSubviews ? see class 1 for example, the view has some commands already that to be not destroyed or replaced.
There is a much simpler way. Your actual goal is to update all views in your app. There is no need to go through each view controller. Simply call your changeFontSize() extension method on your app's main window.
This question already has answers here:
Calling instance method during initialization in Swift
(8 answers)
Closed 6 years ago.
I have a class
class ChartView: UIView
{
class: DotView {
let circleView1: UIView
let circleView2: UIView
init (view: UIView)
{
self.view = view
self.circleView1 = self.buildCircle(some rect here)
self.circleView2 = self.buildCircle(some rect here)
func buildCircle(rect: CGRect) -> UIView
{
let dotView = UIView(frame: rect)
dotView.backgroundColor = UIColor.whiteColor()
dotView.layer.cornerRadius = dotView.bounds.width / 2
self.view.addSubview(dotView)
return dotView
}
}
}
But I got this error:
Use of 'self' in method call 'buildCircle' before all stored properties are initialized
So I just want to create objects in some method and then assign it to the stored properties. How can I fix my code?
You can't call methods on self before all non-optional instance variables are initialized.
There are several ways to go around that.
Change properties to optionals or implicitly unwrapped optionals
(not recommended)
Make the buildCircle() method static or just a
function in the file and call the addSubview() for all the circles
after all of the properties were initialized and you called
super.init()
etc. You just have to avoid calls to self before the
class was initialized.
For solving of this issue it is possible to use these approaches:
class MyClass: NSObject {
var prop: String = ""
override init() {
super.init()
self.setupMyProperty()
}
func setupMyProperty() {
prop = "testValue"
}
}
class MyClass1: NSObject {
var prop: String = ""
override init() {
prop = MyClass1.setupMyProperty()
super.init()
}
class func setupMyProperty() -> String{
return "testValue"
}
}
You can create your circleView in the function willMoveToSuperview.
In this lifecycle function you are sure to have ChartView allocated properly, so:
override func willMoveToSuperview(newSuperview: UIView?) {
self.circleView1 = self.buildCircle(some rect here)
self.circleView2 = self.buildCircle(some rect here)
}
I'm making app and I'm trying to make a View which contains a Label with a question. I want this view in my app and because I will use it repeatedly, I made a class (If I want to make some change, I can do It from one place). The UIView is called questionView (var questionView = UIView()). Problem is when I want to make questionView a subview of view. The error says that I don't have have "view" which I understand. I don't have view but how can I get it? Thank you
This is what is inside my Question class:
import Foundation
import UIKit
class Question {
// PROPERTIES:
var questionLabel = UILabel()
var questionView = UIView()
// METHODS:
func createQuestion (input:String) {
// some code .... not important
// THIS:
self.view.addSubview(questionView)
}
// ... next code, also not important
}
UPDATE:
There is my solution. It works BUT I think that it's not correct from a programming standpoint. Can anybody tell me anything about it? Thank you
My class in separate swift file:
My class in separate swift file:
class LabelClass {
var view = UIView()
init (view: UIView) {
self.view = view
}
var lbl = UILabel()
var lblView = UIView()
func makeLabel () {
self.lbl.frame = CGRectMake(0, 0, 150, 50)
self.lbl.text = "Text text text"
self.lbl.numberOfLines = 0
self.lblView.frame = CGRectMake(20, 20, 150, 50)
self.lblView.addSubview(self.lbl)
self.view.addSubview(lblView)
}
}
Piece of code my ViewController.swift:
override func viewDidLoad() {
super.viewDidLoad()
// Added code:
var object = LabelClass(view: self.view)
object.makeLabel()
}
I don't know Swift, but as far as I know, only instances of UIViewController have a view property, the class Question does not, so you cannot add subviews to it.
What you probably want is making a subclass of UIView which contains a question label, or to add the questionLabel as a subview of questionView.
It is because you are trying to add your view to a normal Swift class which doesn't have a self.view instance. Your Question class must be a subclass of UIViewController cocoa class that it has a self.view instance and override methods.
class Question:UIViewController {
// PROPHERITIES:
var questionLabel = UILabel()
var questionView = UIView()
// METHODS:
override func viewDidLoad() {
createQuestion("foo")
}
func createQuestion (input:String) {
// some code .... not important
// THIS:
self.view.addSubview(questionView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// ... next code, also not important
}
I need to specify one textView in which I want to update the text. I have tried the following:
func updateView(message: String) {
var textView2 = UITextView.viewWithTag(2)
textView2.text = message
}
but I receive this error:
type 'UIView' does not conform to protocol 'IntegerLiteralConvertable
You are doing it wrong. You are trying to get textview's subview, which is not correct. Instead, you should query superview for UITextView. It should be:
func updateView(message: String) {
var textView2 = self.view.viewWithTag(2) as UITextView;
textView2.text = message
}
In swift, you have to typecast and moreover you have to provide the superview
var textView2 : UITextView? = self.view.viewWithTag(2) as? UITextView;
So your function should be like this:-
func updateView(message: String) {
var textView2 : UITextView? = self.view.viewWithTag(2) as? UITextView;
textView2.text = message
}
If viewWithTag() is used to "find" a component from a Storyboard, it's good to know about IBOutlets:
http://codewithchris.com/9-hooking-it-all-up-swift-iboutlet-properties/
Around 2:30