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.
Related
I have a View Controller embedded in Navigation Controller. The view has 1 WKWebView, hence, I'm setting view = webView in loadView() override.
So, I'm adding a small little sub navigation bar underneath my navigation controller to allow a user to change their location.I can add the subview to the navigation controller, I'm just not able to make it clickable.
override func loadView() {
let config = WKWebViewConfiguration()
config.processPool = YourModelObject.sharedInstance.processPool
webView = WKWebView(frame: .zero, configuration: config)
webView.navigationDelegate = self
self.webView.scrollView.delegate = self
view = webView
..
if let navigationBar = self.navigationController?.navigationBar {
let secondFrame = CGRect(x: 50, y: 44.1, width: navigationBar.frame.width, height: 30)
let secondLabel = UILabel(frame: secondFrame)
secondLabel.textColor = .black
secondLabel.text = "Getting your location..."
secondLabel.isUserInteractionEnabled = true
let guestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(setLocation(_:)))
secondLabel.addGestureRecognizer(guestureRecognizer)
secondLabel.textAlignment = .left
secondLabel.font = secondLabel.font.withSize(14)
secondLabel.tag = 1002
navigationBar.addSubview(secondLabel)
}
}
And then the setLocation function
#objc func setLocation(_ sender: Any) {
print("location label tapped")
}
But when I tap the label, I'm not getting anything printed in console. I don't know if the use of target: self is wrong for the tapGestureRecognizer or what's going on here.
I too am new to Swift, so my answer is far from guaranteed. I just know what it's like to be in your position,
Perhaps try creating a subclass of navigationBar for the sub navigation bar, i.e. mySubNavigationBar. Then in the subclass's code do all the initialization that you need to do. Including the print line so you'll know if you're getting there.
p.s. I would have put this as a comment, but I don't have enough points to add comments.
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
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.
I've got one of these Views here:
let placeContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.whiteColor()
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 7
view.layer.masksToBounds = true
return view
}()
And I'm looking to create one next to it every time a function "createNewView()" is called.
let showFullPlaceContainerView = UITapGestureRecognizer(target: self, action: #selector(showFullPlaceContainerViewFunction))
placeContainerView.addGestureRecognizer(showFullPlaceContainerView)
That I want each of the placeContainerView's to respond to.
So I want a view to be generated and I'll give it certain values. And then when I call the function createNewView() I'll get a new view next to it that is exactly the same except with whatever new values I put in.
If you've got any ideas please let me know!
Thank you
EDIT:
The code below demonstrates how I want to setup the placeContainerView each time but I need them to be displayed so that the placeContainerView.topAnchor() is different each time.. How exactly does that work if it is kept in its own class and doesn't know how many times it has been created?
Also, as placeContainerView contains placeLabel and placeImageView do these have to be generated inside the new PlaceContainerViewClass as well?
func setupPlaceContainerView() {
placeContainerView.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: -180).active = true
placeContainerView.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 80).active = true
placeContainerView.widthAnchor.constraintEqualToConstant(227).active = true
placeContainerView.heightAnchor.constraintEqualToConstant(45).active = true
placeContainerView.addSubview(placeLabel)
placeContainerView.addSubview(placeImageLabelSeparator)
placeContainerView.addSubview(placeImageView)
placeLabel.leftAnchor.constraintEqualToAnchor(placeContainerView.leftAnchor).active = true
placeLabel.topAnchor.constraintEqualToAnchor(placeContainerView.topAnchor).active = true
placeLabel.widthAnchor.constraintEqualToConstant(180).active = true
placeLabel.heightAnchor.constraintEqualToAnchor(placeContainerView.heightAnchor).active = true
placeImageLabelSeparator.leftAnchor.constraintEqualToAnchor(placeLabel.rightAnchor).active = true
placeImageLabelSeparator.topAnchor.constraintEqualToAnchor(placeContainerView.topAnchor).active = true
placeImageLabelSeparator.widthAnchor.constraintEqualToConstant(2).active = true
placeImageLabelSeparator.heightAnchor.constraintEqualToAnchor(placeContainerView.heightAnchor).active = true
placeImageView.leftAnchor.constraintEqualToAnchor(placeImageLabelSeparator.rightAnchor).active = true
placeImageView.topAnchor.constraintEqualToAnchor(placeContainerView.topAnchor).active = true
placeImageView.widthAnchor.constraintEqualToConstant(45).active = true
placeImageView.heightAnchor.constraintEqualToAnchor(placeContainerView.heightAnchor).active = true
As noted by #Paulw11, this task is simply done by creating a new class.
class placeContainerView:UIView {
var x:Double!
var y:Bool!
var z:UILabel!
var controller:UIViewController!
//If you want to pass specific values number, you can use convenience init method OR you can use the default init method they give you.
//previousLabelFrame:CGrect = CGRect() // I defaulted all these values to 0, make them whatevree u need. You would use the default one for the first Label you would make. Then after that, you would pass in the previous one made, to get the frame of it so you can add to the one after that.
convenience init(x:Double,y:Bool, z:UILabel, previousLabelFrame:CGRect = CGRect(x: 0, y:0, width:0, height:0), VC:UIViewController) {
self.x = x
self.y = y
self.z = z
self.controller = VC
let distance = self.controller.width*0.1 //Whatever u decide here
//You could just do CGRect(x:previousLabelFrame.maxX+distance, depeding on what you need.
self.frame = CGRect(x: previousLabelFrame.minX+distance, y:previousLabelFrame.minY, width: previousLabelFrame.width, height:previousLabelFrame.height)
}
}
Usage inside ViewController:
var views:[placeContainerView] = []
let view:placeContainerView = placeContainerView(10, true, UILabel(),views[views.count-1], self)
self.views.append(view)
//OR if this is the FIRST placeContainerView of the whole app, it will use the default values for the frame.
let view:placeContainerView = placeContainerView(10, true, UILabel(), self)
self.views.append(view)
Some odd example of how to use.
Then everytime they click a button, just make a new placeContainerView
I want to copy one UIView to another view without making it archive or unarchive.
Please help me if you have any solution.
I tried with by making an extension of UIView as already available an answer on Stack over flow. But its crashing when I pass the view with pattern Image Background color.
The code related to my comment below:
extension UIView
{
func copyView() -> UIView?
{
return NSKeyedUnarchiver.unarchiveObjectWithData(NSKeyedArchiver.archivedDataWithRootObject(self)) as? UIView
}
}
I've just tried this simple code in a Playground to check that the copy view works and it's not pointing the same view:
let originalView = UIView(frame: CGRectMake(0, 0, 100, 50));
originalView.backgroundColor = UIColor.redColor();
let originalLabel = UILabel(frame: originalView.frame);
originalLabel.text = "Hi";
originalLabel.backgroundColor = UIColor.whiteColor();
originalView.addSubview(originalLabel);
let copyView = originalView.copyView();
let copyLabel = copyView?.subviews[0] as! UILabel;
originalView.backgroundColor = UIColor.blackColor();
originalLabel.text = "Hola";
originalView.backgroundColor; // Returns black
originalLabel.text; // Returns "Hola"
copyView!.backgroundColor; // Returns red
copyLabel.text; // Returns "Hi"
If the extension wouldn't work, both copyView and originalView would have same backgroundColor and the same would happen to the text of the labels. So maybe there is the possibility that the problem is in other part.
Original Post
func copyView(viewforCopy: UIView) -> UIView {
viewforCopy.hidden = false //The copy not works if is hidden, just prevention
let viewCopy = viewforCopy.snapshotViewAfterScreenUpdates(true)
viewforCopy.hidden = true
return viewCopy
}
Updated for Swift 4
func copyView(viewforCopy: UIView) -> UIView {
viewforCopy.isHidden = false //The copy not works if is hidden, just prevention
let viewCopy = viewforCopy.snapshotView(afterScreenUpdates: true)
viewforCopy.isHidden = true
return viewCopy!
}