Yesterday I was reading about Protocols, and how to powerful they're. One of the answers was here in Stack Overflow saying:
"You can think of protocol if you said a class has a feature, and a superclass when you say my child class is a thing"
So, in my situations, I have 3 UIViewControllers, each has a UIPickerView. Thus, I thought of making a protocol that adds a UIPickerView for conformed classes of type UIViewController.
I have created a playground just to test working with UIView throughout protocols.
My issues were:
a) Couldn't create UIViews with code, so I created a function to create UIViews.
b) Couldn't add constraints because the views are inside the function.
It's worked fine before I set translatesAutoresizingMaskIntoConstraints to false. But I need to add constraints by myself.
Finally, my questions is:
I want to reach to point where a UIViewController just conform to a protocol and has a UIView implemented in it without adding extra code to it. How is that possible? and if not, what is the best thing to do to reach to what I'm looking for.
I have created the following playground for tests:
import UIKit
import PlaygroundSupport
protocol Labelable {
func label() -> UILabel
}
extension Labelable where Self: UIViewController {
func label() -> UILabel {
let helloLabel = UILabel()
helloLabel.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
helloLabel.text = "Hello World!"
helloLabel.textColor = .black
//helloLabel.translatesAutoresizingMaskIntoConstraints = false
//helloLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
//helloLabel.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
//helloLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
//helloLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
return helloLabel
}
}
class MyViewController: UIViewController, Labelable {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let lab = label()
view.addSubview(lab)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = UINavigationController(rootViewController: MyViewController())
Note that uncommenting the constraints lines makes a huge number of calls to the protocol method.
Related
This may be the simplest thing you can possibly due in Xcode in Swift and for some reason, it is not working properly.
I want to center a label in a view. The only other thing in the view previously was a webView added programatically but for now I have removed that so basically, I have an empty VC in which I'm trying to center a label.
There are umpteen answers on SO about this and I've tried every combination but can't get it to to work.
Can anyone suggest a foolproof way to accomplish the simple task of centering a UILabel?
Below is the code I currently have and steps I've taken along with result:
I created an empty view controller in Storyboard and embedded it in a navigation controller.
I set the View Controller in Storyboard to my swift VC class. I also have already cleaned project, closed and re-opened XCode and also deleted storyboard and recreated it in case it was corrupted. Still nothing works.
myVC.swift
import UIKit
class myVC: UIViewController,WKScriptMessageHandler, WKNavigationDelegate,WKUIDelegate {
var title= "Hello there"
var loadingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
webView.uiDelegate = self
loadingLabel.translatesAutoresizingMaskIntoConstraints = false
// loadingLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
// loadingLabel.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
// loadingLabel = UILabel(frame: CGRect(x: 0, y: self.view.center.y, width: 290, height: 70))
loadingLabel.center = self.view.center
loadingLabel.textAlignment = .center
loadingLabel.font = UIFont(name: "Halvetica", size: 18.0)
loadingLabel.numberOfLines = 0
loadingLabel.text = "TEXT I WANT TO CENTER"
loadingLabel.lineBreakMode = .byTruncatingTail
loadingLabel.center = self.view.center
self.view.addSubview(loadingLabel)
self.title = title
}
override func loadView() {
super.loadView()
}
}
Add the loadingLabel as subview before adding the constraints.
view.addSubview(loadingLabel)
loadingLabel.translatesAutoresizingMaskIntoConstraints = false
loadingLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
loadingLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
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.
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.
I want to create and customize a JTAppleCalendarView using only Swif 3 code, i.e. without the Interface Builder, Storyboards or XIB files.
I can create customs labels by code, however when I try to create a JTAppleCalendarView, I simply can not change the frame value, neither the heightAnchor or widthAnchor. This way, my calendar is not displayed.
I am attaching the code of the init of the my custom UIViewController (which implements the Datasource and Delegate protocols):
init(frame: CGRect) {
super.init(nibName: nil, bundle: nil)]
self.view = UIView(frame: frame)
self.view.backgroundColor = UIColor.green
let margins = self.view.layoutMarginsGuide
let calendar = JTAppleCalendarView()
print("CalendarView frame: ", calendar.frame)
calendar.dataSource = self
calendar.delegate = self
calendar.cellInset = CGPoint(x: 0, y: 0)
calendar.backgroundColor = UIColor.white
calendar.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(calendar)
calendar.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
calendar.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
calendar.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
//calendar.widthAnchor.constraint(equalTo: margins.widthAnchor).isActive = true
calendar.heightAnchor.constraint(equalToConstant: 400).isActive = true
}
Whenever I change calendar.heightAnchor, calendar.widthAnchor or calendar.frame, I get the error "Unexpectedly found nil while unwrapping an Optional value.".
I followed the library tutorial, however they only presents the creation using Interface Builders, which I am not using in my project. Tecnically, the component is just a custom UIView, so I am confused with this error.
In the code above, I forgot to register the cell.
Solved registering the cell with something like: calendar.registerCellViewClass(type: SomeCellClass.self).
So far, it worked for a simple project. I did not tested with playgrounds.
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
}