How to make UI elements in class out of View Controller - ios

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
}

Related

Programmatically assigning class to a UIView created in Storyboard (Swift)

Question: How do I programmatically assign a class to a UIView created in Storyboard?
I have a UIView created in IB (graphView) in which I would like to display one of several graphs, their definitions created in their respective classes.
To do this, I created another view programmatically then assigned the class of the selected graph to that view, then added it to the original view:
#IBOutlet weak var graphView: UIView!
func displayGraph(choice: Int) {
var selectedGraphView = UIView()
switch choice {
case 1: selectedGraphView = class1()
case 2: selectedGraphView = class2()
case 3: selectedGraphView = class3()
default: selectedGraphView = class1()
}
selectedGraphView.frame = CGRect(x: 0, y: 0, width: graphView, height: graphView)
graphView.addSubview(selectedGraphView)
}
Things seem to work, but do I really need that second UIView (selectedGraphView)?
I tried changing it to:
func displayGraph2(choice: Int) {
switch choice {
case 1: graphView = class1()
case 2: graphView = class2()
case 3: graphView = class3()
default: graphView = class1()
}
}
but I don't know how to get the contents of the class to run.
Can I do it this way? If so, what is missing?
EDIT: Actually, there are 3 UIViews: the one in IB, the one to hold the graph, and the class itself creates one.
If, say class1 has nothing but print("entered class1"), in the first case (displayGraph), I see "entered class1" printed. In the second (displayGraph2), I get nothing.
EDIT2:
I can assign any of the classes to graphView in IB and get the print message from the class. How do I assign a class to graphView programmatically (since what I attempted in displayGraph2 doesn't work)?
When you add a UIView in Storyboard, you can assign its Custom Class. However, at run-time, you cannot change its class this way:
#IBOutlet var graphViewHolder: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// cannot do this
let v = View2Class()
graphViewHolder = v
}
One common approach, as you've done, is to add a UIView in your Storyboard as a "holder" (or "container") view. Then, in code, instantiate an instance of your desired Custom Class and add it as a subview to the "holder" view.
However, if you do this:
selectedGraphView.frame = CGRect(x: 0, y: 0, width: graphView, height: graphView)
graphView.addSubview(selectedGraphView)
The newly added subview will have the size of the "holder" view, and you'll run into layout issues if / when the holder view changes size (such as on device rotation).
So, you can do it like this:
// let's name it "graphViewHolder" so we know we're using it as a "holder" view
#IBOutlet var graphViewHolder: UIView!
var currentChoice: Int = 1
override func viewDidLoad() {
super.viewDidLoad()
displayGraph(choice: 1)
}
func displayGraph(choice: Int) {
var selectedGraphView: UIView?
switch choice {
case 1: selectedGraphView = View1Class()
case 2: selectedGraphView = View2Class()
case 3: selectedGraphView = View3Class()
default: selectedGraphView = View1Class()
}
// unwrap optional
guard let sgv = selectedGraphView else { return }
// does the "holder" view already have any subviews?
// if so, remove them
graphViewHolder.subviews.forEach { v in
v.removeFromSuperview()
}
// add the new view as a subview of the "holder" view
graphViewHolder.addSubview(sgv)
// we need to give the new view auto-layout properties
sgv.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// constrain it to all 4 sides of the "holder" view
sgv.topAnchor.constraint(equalTo: graphViewHolder.topAnchor),
sgv.leadingAnchor.constraint(equalTo: graphViewHolder.leadingAnchor),
sgv.trailingAnchor.constraint(equalTo: graphViewHolder.trailingAnchor),
sgv.bottomAnchor.constraint(equalTo: graphViewHolder.bottomAnchor),
])
}
Now the subview will follow the "holder" view's constraints.
Note that if you want to do something to the loaded view, you'll have to make sure it's the right class.
So, for example, if we want to call a custom func in View2Class:
// make sure the "holder" view has a subview
guard let v = graphViewHolder.subviews.first else {
print("Graph View Holder has no subviews!")
return
}
// make sure the subview is a View2Class instance
guard let v2 = v as? View2Class else {
print("The Holder subview is not View2Class!")
return
}
// call a func in View2Class
v2.myFunc()

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.

Add imageview to all viewcontrollers programmatically

there is a requirement like add an imageview to all viewcontrollers but I have 150+ xib's and it is time consuming to put imageview in every single xib.
Is there a common way to do it? I googled but nothing useful found.
Any help would be appreciated.
It will be easy if you use base class like this
class BaseVC: UIViewController
{
var imageView:UIImageView = UIImageView.init()
override func viewDidLoad() {
super.viewDidLoad()
addImageView()
}
override func viewWillAppear(_ animated: Bool) {
//self.view.bringSubview(toFront: imageView) //To bring imageview infront of other views put this method as per your requirement
}
func addImageView(name:String = "default")
{
let image = UIImage(named: name)
imageView.image = image
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
//view.addSubview(imageView)
view.insertSubview(imageView, at: 0) /*For put image view below all image*/
}
}
You need to derive all your view controller from this like this
class YourVC: BaseVC
also you can change the image with different viewcontrollers.
like
class YourVC: BaseVC
{
override func viewDidLoad() {
super.viewDidLoad()
addImageView(name:"xyz")
}
}
you can write an extension for UIView to add imageview into it and run it into every your viewcontrollers.
extension UIView {
func addImage() {
let imageView = UIImageView(frame: frame)
imageView.image = UIImage(named: "Your Image Name")
addSubview(imageView)
imageView.didMoveToSuperview()
}
}
Create New ViewController without storyboard.
import UIKit
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setImageView()
}
func setImageView() {
let thisImageView = UIImageView(frame: CGRect(x: 10, y: 10, width: 70, height: 70))
thisImageView.image = UIImage(named: "your image name")
view.addSubview(thisImageView)
}
}
now you can set this BaseViewController as delegate of your project's ViewControllers.
import UIKit
class MainViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
now you have this ImageView in MainViewController and all ViewControllers have BaseViewContoller as delegate.
Hope to be useful. Also sorry about my English.
TL;DR No. Based on my understanding of what you are imagining, no you can't write a function to add a UIImageView to every one of your viewControllers.
Long answer:
You need to create a separate controller swift file for each View and set it up in that file. You could create a supporting file in which you setup up the the ImageView then call in in the viewDidLoad for each view controller. (You could even make this an extension of UIView so you can just call something like self.setUpImageView())
My personal recommendation would be to drop the xibs as soon as you can and recreate everything pragmatically, I know it's a headache to throw all your work away but it is really worth it in the end on top of just being good practice. I have a file that I found that makes autolayout a breeze that I can share with you if you'd like. I used to really enjoy storyboards and xibs myself but they are a hassle that just isn't worth it anymore and cause such a headache in situation like this.

Access TextField from another class or method without storyboard

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

how to force run a method in all viewControllers viewDidLayoutSubviews functions automatically?

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.

Resources