How to reference UILabel strong reference programmatically using Swift? - swift

Using Swift, I have a UILabel that was created programmatically in the viewDidLoad override function:
var middleDate = UILabel(frame: CGRect(x: (screenWidth / 2) - 45, y: 0, width: 90, height: 20))
middleDate.text = "3:13:46 PM"
middleDate.font = UIFont(name: "ArialMT", size: 13)
middleDate.textAlignment = .Center
Which works great.
Then I am trying to change the text in the override function viewDidAppear.
I get Build Failed with the message Use of unresolved identifier 'middleDate'
If I use interface builder and drag and drop a UILabel on the view, then give it a strong reference it works great. Is there a way to reference a programmatically created object with a strong reference?
Or am I going about this incorrect?
This is what I have on my ViewController.swift:
I have a feeling that there is a much better and cleaner way to do this.
override func viewDidLoad() {
super.viewDidLoad()
var middleDate = UILabel(frame: CGRect(x: (screenWidth / 2) - 45, y: 0, width: 90, height: 20))
middleDate.text = "3:13:46 PM"
middleDate.font = UIFont(name: "ArialMT", size: 13)
middleDate.textAlignment = .Center
self.myTopView.addSubview(middleDate)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
var timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: Selector("getLocalTime:"),
userInfo: nil,
repeats: true)
let mainLoop = NSRunLoop.mainRunLoop()
mainLoop.addTimer(timer, forMode: NSDefaultRunLoopMode)
func getLocalTime(timer: NSTimer) {
var todaysDate:NSDate = NSDate()
var dateFormatter:NSDateFormatter = NSDateFormatter()
var centerDateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "h:mm:ss"
var DateInFormat:String = dateFormatter.stringFromDate(todaysDate)
middleDate.text = "hello"
}
}

Try this:
class ViewController: UIViewController {
var middleDate = UILabel(frame: CGRect(x: (360 / 2) - 45, y: 200, width: 90, height: 20))
override func viewDidLoad() {
super.viewDidLoad()
middleDate.text = "3:13:46 PM"
middleDate.font = UIFont(name: "ArialMT", size: 13)
middleDate.textAlignment = .Center
self.view.addSubview(middleDate)
}
override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated)
middleDate.text = "hello"
}
}
Your issue was middleDate was declared with local scope within the viewDidLoad function. In order for it to be a class variable you need to declare it at the class level. I simplified the class a little just to make it work quickly for me.

Related

Defining a #selector method when it can't access a programmatically created UILabel

If you define a UILabel programmatically, it must be done so in viewDidLoad()? But, if you were to define a #selector method that needed to set said label, it would be impossible given that the #objc selector method needs to be outside the scope of the viewDidLoad() method, where it cannot access the label. How do you get around this?
class ViewController: UIViewController {
var count = 4
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
label.center = CGPoint(x: 250, y: 430)
label.font = .systemFont(ofSize: 130)
label.textColor = UIColor.black
view.addSubview(label)
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: <#T##Selector#>, userInfo: <#T##Any?#>, repeats: <#T##Bool#>)
#objc func updateLabel() { // ERROR #objc can only be used with members of classes...
if count > 0 {
count -= 1
label.text = "\(count)"
}
}
}
}
Your mistake is that you have put the selector updateLabel inside of viewDidLoad. You should put it outside viewDidLoad so that it is a member of your class, rather than a local function.
To access the label in updateLabel, simply move the declaration of the label outside of both methods.
You should also add a timer parameter to updateLabel so that you can stop the timer when count reaches 0, rather than keeping the timer running forever.
var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label = UILabel(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
label.center = CGPoint(x: 250, y: 430)
label.font = .systemFont(ofSize: 130)
label.textColor = UIColor.black
view.addSubview(label)
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateLabel), userInfo: nil, repeats: true)
}
#objc func updateLabel(_ timer: Timer) {
if count > 0 {
count -= 1
label.text = "\(count)"
} else {
timer.invalidate()
}
}

use 2 different UIPanGestureRecongizer on 2 different imageviews in a viewController

my code below initializes 2 different imageviews pg and tim. I have create a seperate var for the UIPanGestureRecognizer to be used for each imageview and func to move the imageview around using UIPanGestureRecongizer. Right now I can not move the imageviews around. When I click on pg it disapers. I just want to develop a function that allows me to move each imageview around freely. All of my code is programmatically so you can just copy the code.
import UIKit
class ViewController: UIViewController {
var pg = UIImageView()
var pgGesture = UIPanGestureRecognizer()
var pgCon = [NSLayoutConstraint]()
var tim = UIImageView()
var timGesture = UIPanGestureRecognizer()
var timCon = [NSLayoutConstraint]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
[pg,tim].forEach({
$0.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview($0)
$0.backgroundColor = UIColor.systemPink
$0.isUserInteractionEnabled = true
})
pgGesture = UIPanGestureRecognizer(target: self, action: #selector(ViewController.pgGestureMethod(_:)))
pg.addGestureRecognizer(pgGesture)
timGesture = UIPanGestureRecognizer(target: self, action: #selector(ViewController.timGestureMethod(_:)))
tim.addGestureRecognizer(timGesture)
pgCon = [
pg.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant :37.5),
pg.topAnchor.constraint(equalTo: view.centerYAnchor, constant : 225),
pg.widthAnchor.constraint(equalToConstant: 75),
pg.heightAnchor.constraint(equalToConstant: 50),
]
NSLayoutConstraint.activate(pgCon)
timCon = [
tim.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant :120),
tim.topAnchor.constraint(equalTo: view.centerYAnchor, constant : 225),
tim.widthAnchor.constraint(equalToConstant: 75),
tim.heightAnchor.constraint(equalToConstant: 50),
]
NSLayoutConstraint.activate(timCon)
}
#objc func pgGestureMethod(_ sender: UIPanGestureRecognizer){
NSLayoutConstraint.deactivate(pgCon)
NSLayoutConstraint.deactivate(timCon)
self.view.bringSubviewToFront(pg)
let tranistioon = sender.translation(in: self.view)
pg.center = CGPoint(x: pg.center.x + tranistioon.x, y: pg.center.y + tranistioon.y)
sender.setTranslation(CGPoint.zero,in: self.view)
}
#objc func timGestureMethod(_ sender: UIPanGestureRecognizer){
NSLayoutConstraint.deactivate(timCon)
NSLayoutConstraint.deactivate(pgCon)
self.view.bringSubviewToFront(tim)
let tranistioon = sender.translation(in: self.view)
tim.center = CGPoint(x: tim.center.x + tranistioon.x, y: tim.center.y + tranistioon.y)
sender.setTranslation(CGPoint.zero,in: self.view)
}
}
Instead of constraints, try giving the frame to both imageViews, i.e.
override func viewDidLoad() {
super.viewDidLoad()
pg.frame = CGRect(x: 100, y: 100, width: 75, height: 50) //here....
tim.frame = CGRect(x: 200, y: 200, width: 75, height: 50) //here....
//rest of the code...
}
In the above code,
change the frame as per your requirement.
from viewDidLoad() remove the code for applying constraints.

UITextField underline width issue

I have a UITextField & UIButton placed inside a vertical UIStackView. Programatically, I've added a UIView to serve as an underline of the UITextField.
The problem comes when I compile the code. The line is longer than the text field. I've printed the width of all of the objects above and it says 335.
I call this function in viewWillAppear and the line is longer. If I call it in viewDidAppear, the line is the exact width of the UITextField but you can see the button and text field flash very briefly before updating the view. What am I doing wrong?
override func viewDidLoad() {
super.viewDidLoad()
observeKeyboardNotifications()
textFieldDelegate()
setupRegisterButton()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupTextFields()
}
func setupTextFields() {
emailTextField.leftViewMode = .always
let emailIconView = UIImageView()
emailIconView.frame = CGRect(x: 0, y: 0, width: 23, height: 17)
emailIconView.image = UIImage(named: "icon_email")
let bgIconView = UIView(frame: CGRect(x: 0, y: 0, width: 28, height: 17))
bgIconView.addSubview(emailIconView)
emailTextField.leftView = bgIconView
emailTextField.tintColor = .white
emailTextField.textColor = .white
emailTextField.backgroundColor = .red
emailTextField.borderStyle = .roundedRect
let bottomLineEmail = CALayer()
bottomLineEmail.frame = CGRect(x: 0, y: 29, width: emailTextField.frame.width, height: 1)
print("Email width", emailTextField.frame.width)
print("button width", retrievePasswordButton.frame.width)
print("line width", bottomLineEmail.frame.width)
bottomLineEmail.backgroundColor = UIColor.white.cgColor
emailTextField.layer.addSublayer(bottomLineEmail)
//emailTextField.backgroundColor = .clear
emailTextField.attributedPlaceholder = NSAttributedString(string : emailTextField.placeholder!,
attributes : [NSAttributedStringKey.foregroundColor: Colors.authTextFieldPlaceholderColor])
retrievePasswordButton.isEnabled = false
handleTextFields()
}
I checked it personally
it works in ViewWillLayoutSubviews
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews();
self.setupTextFields()
}
and viewDidAppear
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.setupTextFields();
}

Updating UILabel with UISlider throwing "unresolved identifier" error for Sender call

The code below is the only code I am using. I have programmatically built a UISlider with a label that I want to update. I've written a function that should call the sender of the UISlider and update the label field to the value of the slider. But it keeps throwing the "unresolved identifier" error for the variable name of the label. Where do I place the function so it can access the label value? I've tried everything.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let mySlider = UISlider(frame:CGRect(x: 60, y:550, width: 300, height: 20))
mySlider.minimumValue = 5
mySlider.maximumValue = 60
mySlider.isContinuous = true
mySlider.tintColor = .white
mySlider.addTarget(self, action: #selector(ViewController.sliderValueDidChange(_:)), for: .valueChanged)
let minuteLabel = UILabel(frame: CGRect(1500, 2000, 150, 1200))
minuteLabel.center = CGPoint(200, 350)
minuteLabel.textAlignment = .center
minuteLabel.font = UIFont(name: minuteLabel.font.fontName, size:250)
mySlider.addTarget(self, action: #selector(ViewController.sliderValueDidChange(_:)),for: .valueChanged)
minuteLabel.textColor = UIColor.white
minuteLabel.text = "\(mySlider.value)"
self.view.addSubview(minuteLabel)
self.view.addSubview(mySlider)
}
public func sliderValueDidChange(_sender: UISlider) {
minuteLabel.text = "\(_sender.value)"
}
}
It's because you create minuteLabel in viewDidLoad() but not assigning to any property inside your ViewController.
You can just create property minuteLabel inside class and assign your minuteLabel from viewDidLoad() to this.
class ViewController: UIViewController {
private var minuteLabel: UILabel?
override func viewDidLoad()
{
super.viewDidLoad()
let mySlider = UISlider(frame:CGRect(x: 60, y:550, width: 300, height: 20))
mySlider.minimumValue = 5
mySlider.maximumValue = 60
mySlider.isContinuous = true
mySlider.tintColor = .white
mySlider.addTarget(self, action: #selector(ViewController.sliderValueDidChange(_:)), for: .valueChanged)
let minuteLabel = UILabel(frame: CGRect(1500, 2000, 150, 1200))
minuteLabel.center = CGPoint(200, 350)
minuteLabel.textAlignment = .center
minuteLabel.font = UIFont(name: minuteLabel.font.fontName, size:250)
mySlider.addTarget(self, action: #selector(ViewController.sliderValueDidChange(_:)),for: .valueChanged)
minuteLabel.textColor = UIColor.white
minuteLabel.text = "\(mySlider.value)"
self.view.addSubview(minuteLabel)
self.view.addSubview(mySlider)
self.minuteLabel = minuteLabel // assign to class property
}
public func sliderValueDidChange(_sender: UISlider) {
minuteLabel?.text = "\(_sender.value)"
}
}

Prog created Buttons from class don't appear - Swift IOS

I'm trying to add a number of customClass buttons to my view automatically, based on the size of an array.
Created the class and called the appropriate method in the class, but nothing shows up. Debugging tells me that the method is called/executed as expected (3x).
When I add the function directly to the view controller it does work.
What am I missing here ???
ViewController code:
import UIKit
class ViewController: UIViewController {
let userArray: [String] = ["One","Two","Three"]
override func viewDidLoad() {
super.viewDidLoad()
for item in userArray {
CustomCheckBox().showNewButton()
}
}
.. Other stuff...
}
CustomButton class code:
{
import UIKit
class CustomCheckBox: UIButton {
let checkedImage: UIImage = UIImage(named:"chckbox_on")!
let uncheckedImage: UIImage = UIImage(named: "chckbox_off")!
var newButton: CustomCheckBox!
..... other functions (isChecked, buttonClicked, ..)
func showNewButton (){
newButton = CustomCheckBox (type: UIButtonType.Custom)
newButton.bounds = CGRect(x: 0, y: 0, width: 45, height: 45)
newButton.center = CGPoint(x: 40, y: 40)
newButton.addTarget(newButton, action: #selector(CustomCheckBox.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
newButton.isChecked=false
self.addSubview(newButton)
}
}
May consider restructuring your code like this
class CustomCheckBoxContainer: UIView {
var newButton: CustomCheckBox!
func showNewButton (){
newButton = CustomCheckBox (type: UIButtonType.custom)
newButton.bounds = CGRect(x: 0, y: 0, width: 45, height: 45)
newButton.center = CGPoint(x: 40, y: 40)
newButton.addTarget(newButton, action: #selector(CustomCheckBox.buttonClicked(_:)), for: UIControlEvents.TouchUpInside)
newButton.isChecked=false
self.addSubview(newButton)
}
}
class CustomCheckBox: UIButton {
let checkedImage: UIImage = UIImage(named:"chckbox_on")!
let uncheckedImage: UIImage = UIImage(named: "chckbox_off")!
//add here all your button functionality
}
and then change your view did load to something like this:
override func viewDidLoad() {
super.viewDidLoad()
for item in userArray {
let customCheckBoxContainer = CustomCheckBoxContainer()
customCheckBoxContainer.showNewButton()
self.view.addSubview(customCheckBoxContainer)
}
}
pass your current View as parameter of your function like this.
override func viewDidLoad() {
super.viewDidLoad()
for item in userArray {
CustomCheckBox().showNewButton(self.view)
}
}
Modify your custom UIButton function newButton like this
func showNewButton (currentView : UIView){
newButton = CustomCheckBox (type: UIButtonType.Custom)
newButton.bounds = CGRect(x: 0, y: 0, width: 45, height: 45)
newButton.center = CGPoint(x: 40, y: 40)
newButton.addTarget(newButton, action: #selector(CustomCheckBox.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
newButton.isChecked=false
currentView.addSubview(newButton)
}