Access TextField from another class or method without storyboard - ios

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
}
}

Related

Update subviews of UIView from ViewController

I have a UIViewController that implements a custom UIView, so;
override func loadView() {
view = CustomView()
}
The custom view has a few lables and buttons and all the normal stuff, problem is in my viewController I have a request, and when that request is done, I'd like to update some of those lables/buttons.
Right now, in my CustomView, I have functions, such as;
func updateView() {
labelOne.isHidden = true
LabelTwo.isHidden = false
}
So I call the appropriate function from my viewController when the request is done.
This works, but it feels wrong, is there a neater way to update the subviews of my custom UIView, from my viewController? Should I maybe be using protocols or delegates?
One thing I've found quite neat in the past is passing the model directly to the custom view, then using didSet to trigger updates.
class CustomView: UIView {
let labelOne = UILabel()
let labelTwo = UILabel()
var object:CustomObject! {
didSet {
self.labelOne.text = object.name
self.labelTwo.text = object.description
}
}
...
}
This means in your UIViewController you can do the request and then pass the model straight to the custom view.
RequestHelper.getObject() { object in
self.customView.object = object
}
Obviously here I'm guessing at your request and object names but hopefully you get the idea.

How to Updatie a uitextview from an external source.

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.

How to add property to UIButton?

thanks for all help:)! fixed it using iboutlet collection and add properies on viewDidLoad
I'm trying to add properties to keyboard keys like layer.shadowColor or layer.shadowRadius.
I got an error
'Value of type '(UIButton)' -> () has no member 'layer'
how to fix this ?
this is my code keyboardViewController.swift
import UIKit
class KeyboardViewController: UIInputViewController {
var newKeyboardView: UIView!
#IBAction func keyPressed(sender: UIButton) {
}
#IBOutlet var nextKeyboardButton: UIButton!
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func viewDidLoad() {
super.viewDidLoad()
loadInterface()
}
func loadInterface() {
// load the nib file
let keyboardNib = UINib(nibName: "newKeyboard", bundle: nil)
// instantiate the view
newKeyboardView = keyboardNib.instantiateWithOwner(self, options: nil)[0] as! UIView
// add the interface to the main view
view.addSubview(newKeyboardView)
// copy the background color
view.backgroundColor = newKeyboardView.backgroundColor
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
override func textWillChange(textInput: UITextInput?) {
// The app is about to change the document's contents. Perform any preparation here.
}
override func textDidChange(textInput: UITextInput?) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
let proxy = self.textDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
self.nextKeyboardButton.setTitleColor(textColor, forState: .Normal)
}
}
I think that in order to apply some style to the button, you need an outlet to this button.
Right now, from what I can understand, you are trying to apply styles to the button from the #IBAction to the sender, which is not the proper way to do it.
Try to make an outlet to the button in the view controller and then to apply the styles from within the viewDidLoad method.
I hope this is clear, but if you want a more specific answer you need to show us what you tried, for example pasting the code you have in the view controller
EDIT:
Based on the code you post, the keyboard is a Nib you instantiate from loadInterface(). I don't have a clear vision of the whole thing with only this piece of code, but it seems to me that you are trying to apply some styles to every key button of a keyboard view. Unfortunately this really depends on how the keyboard is implemented, can you provide some more details?
Anyway, from what I see I think you didn't write this code: probably you are following a tutorial or maintaining someone else's code. That's ok, but I suggest you to follow a an introduction course to iOS development with Swift, like the Udacity's one, which is fantastic IMHO (https://www.udacity.com/course/intro-to-ios-app-development-with-swift--ud585)
If you try to format your UIButton with QuartzCore framework, you'll need to import it first:
import QuartzCore
Then you will be able to access those members.
For example (latest swift3 code):
#IBAction func keyPressed(sender: UIButton) {
let button = sender as UIButton!
button?.backgroundColor = UIColor.red
button?.layer.shadowColor = UIColor.black.cgColor
button?.layer.shadowRadius = 1.0
button?.layer.cornerRadius = 4.0
}
In case you need to apply your styles sooner, try to consider to put this code into viewDidLoad or viewDidAppear methods:
self.nextKeyboardButton.backgroundColor = UIColor.red
self.nextKeyboardButton.layer.shadowColor = UIColor.black.cgColor
self.nextKeyboardButton.layer.shadowRadius = 1.0
self.nextKeyboardButton.layer.cornerRadius = 4.0
Seems like you're trying to "add property" not to a button, but rather to a closure which accepts a button as an argument.
Make it like this:
nextKeyboardButton.layer.shadowColor = UIColor.redColor.cgColor
nextKeyboardButton.layer.shadowRadius = 5.0

When using Computed variable and Snapkit: No common superview between views

So here's the thing, I'm declaring a property like this:
var aNameLabel: UILabel {
guard let foo = Applicant.sharedInstance.realName else {
return UILabel(text: "获取姓名失败", color: .whiteColor())
}
return UILabel(text: foo, color: .whiteColor())
}
And when I try to add constraint to the aNameLabel after I did someView.addSubView(aNameLabel), the app would crash every time at this constraint-adding thing, and says No common superview between views
However, when I change the variable into a let constant like this:
let aNameLabel = UILabel(text: "Allen", color: .whiteColor())
The constraint will be added with no complaint. Somebody can help me with this?
UPDATE
With the help of #par , I've changed my code into this:
var aNameLabel: UILabel = {
guard let foo = Applicant.sharedInstance.realName else {
return UILabel(text: "BAD", color: .whiteColor())
}
return UILabel(text: foo, color: .whiteColor())
}()
And then the aNameLabel would always be assigned with value "BAD", while actually my guard let is successful. How do I fix this?
The problem is that you are creating a new UILabel every time you access the aNameLabel variable (a computed property function runs every time you access it). Presumably you are doing the same thing for the superview of this view (when you access someView in someView.addSubview() in your example above). If so, that's why there's no common superview and you are crashing.
You should create only one instance of each UIView used by your view controller, so creating a variable as a constant as you've shown is a great approach, or you can use a closure-initializer pattern like so:
var aNameLabel: UILabel = {
return UILabel(...)
}()
Notice in the above example the parentheses after the closing brace. Because it's a closure-initializer it will only be called once just like a let constant.
Often a UIView created with let isn't appropriate because constant properties need to be initialized before init() returns, and if you're creating views in a view controller you won't have a chance to add views until loadView() is called. In this case, declare your UIViews as implicitly-unwrapped optionals. They will be set to nil by init() which meets the initialization requirement, then you can set them to actual views later when viewDidLoad() is called, for example:
class MyViewController: UIViewController {
var someSubview: UIView!
override func viewDidLoad() {
super.viewDidLoad()
someSubview = UIView()
view.addSubview(someSubview)
// now set constraints with SnapKit
}
}

iOS adding design-time text on storyboard

Is it possible to set a design-time text for UILabel (or image if using UIImageView) on iOS 8? if so, how to?
Basically what I need is not to empty all of my labels before compiling so that it doesn't show dummy data before loading from the network the actual data. An algorithm to programatically clear all outlets isn't really a good solution as it is unnecessary code.
You could try subclassing the classes you want to have design-time attributes. Here is an example of that for UILabel:
import UIKit
class UIPrototypeLabel: UILabel {
#IBInspectable var isPrototype: Bool = false
override func awakeFromNib() {
if (isPrototype) {
self.text = "test"
}
}
Then, in IB, you will see isPrototype, and you can set it to true or false.
You can also change the default from false to true in the isPrototype:Bool = false line if you want. You can also change what happens if isPrototype is true. I had it make the text "test" so I could see feedback when testing this out, so you could change it to nil or "" or whatever.
You can also just eschew the isPrototype bool and have this class always reset the text. I just thought the IBInspectable attribute was cool, but if you just want this class to always clear the label text then you would just delete the bool and the check and just self.text=nil every time.
The con to this approach is you need to make all of your labels UIPrototypeLabel to get this functionality.
There is a second, scarier approach, that will add this functionality to all of your UILabels, and that is extending UILabel.
import ObjectiveC
import UIKit
// Declare a global var to produce a unique address as the assoc object handle
var AssociatedObjectHandle: UInt8 = 0
extension UILabel {
#IBInspectable var isPrototype:Bool {
get {
var optionalObject:AnyObject? = objc_getAssociatedObject(self, &AssociatedObjectHandle)
if let object:AnyObject = optionalObject {
return object as! Bool
} else {
return false // default value when uninitialized
}
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
public override func awakeFromNib() {
if (isPrototype) {
self.text = "test"
}
}
}
credit to Is there a way to set associated objects in Swift? for some of that code

Resources