What to use as NSCoder to initialize a View? - swift

Hey guys I created a new custom View Class and now I want to build a instance of it, I initialized it with the following code:
required init?(coder aCoder: NSCoder) {
super.init(coder: aCoder)
tapRecognizer = UITapGestureRecognizer(
target: self, action: #selector(handleBarTap))
addGestureRecognizer(tapRecognizer)
}
deinit {
removeGestureRecognizer(tapRecognizer)
}
And this is the instance, but what can I use as coder?
lazy var chartView = TutorialChartView(coder: )
Thanks in advance!

When you say View, do you mean UIView? The problem is that that's the wrong initializer. init(coder:) is not something you call; it's a process initiated by the storyboard when there is one of these things in the storyboard and you load that view controller.
The code UIView designated initializer is init(frame:). Implement that and call it, and you're all set.
(You may also have to provide an implementation of init(coder:) but it should just throw a fatalError, because you do not expect to be called this way.)

You should cannot use coder as initializer for creating class, use frame instead, here is your code written in frame-style initializing.
override init(frame: CGRect) {
super.init(frame: frame)
tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleBarTap))
addGestureRecognizer(tapRecognizer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Here is how you use it:
lazy var chartView = TutorialChartView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)

Related

NSTouchBar not releasing last touched view

I am making an app which uses the NSTouchBar.
The touchbar is made by the NSWindowController.makeTouchBar()method.
In this touchbar I can place NSCustomTouchBarItems.
I have made two NSCustomTouchBarItems.
The first one sets a view to a default ui button, with this:
let item001 = NSCustomTouchBarItem(identifier: someIdentifier)
item001.view = NSButton(title: "myButton", target: nil, action: nil)
The second one sets a viewController, with this:
let item002 = NSCustomTouchBarItem(identifier: someIdentifier)
item002.viewController = TestViewController()
The TestViewController only loads a simple view inside its loadView()
method.
class TestViewController: NSViewController {
override func loadView() {
self.view = TestView001(frame: NSRect(x: 0, y: 0, width: 100, height: 30))
}
}
The TestView001 only creates a background color so you can see it.
TestView001 has the following code:
class TestView001: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
print("TestView001.init()")
// Create a background color.
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.green.cgColor
}
required init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
All of this works perfectly.
But when I have touched the second item inside the touchbar,
and then close my app's window.
The windowController and everything else is nicely released
from memory.
But I can still see that TestView001 is in memory and not being
released.
When using a standard ui button like in item001, then you don't
have this problem.
It looks like some NSTouch still has a reference to the view
if you look at this image:
However, I do not completely understand this image.
What is the best way of solving this.
Thanks in advance.

Crash when using loadNibNamed

This is my code:
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
let mainBundle = Bundle.main
mainBundle.loadNibNamed("iconView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
I copied it from step 8 in this tutorial that I've seen (I only changed the name in the code to my xib file: "iconView").
But for some reason it fails:
I already tried all of the solutions in stackoverflow, but nothing helped me.
Here is my xib file:
I really don't know what to do.
I'm using Xcode 12 beta 6, iOS 14 beta 6
UPDATE
Okay, a lot of people saied in the comments of the tutorial that
This causes an infinite-loop calling commonInit().
So now I know what the issue is, but I still don't know how to solve it. Any ideas?
You probably have to remove Class from your View.
First select your View (in your case iconView):
and then clear whatever there is in the class field:

Property Initialization with closures

I was looking into ARC and strong reference cycles and ran into this code of mine:
class TestClass: UIView {
let button: UIButton = {
let view = UIButton()
view.frame = CGRect(x: 50, y: 50, width: 200, height: 200)
view.backgroundColor = .blue
view.translatesAutoresizingMaskIntoConstraints = false
view.setTitle("Button", for: .normal)
view.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
return view
}()
#objc private func buttonClicked() {
print("Clicked")
}
override init(frame: CGRect) {
super.init(frame: frame)
print("Object of TestClass initialized")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
print("Object of TestClass deinitialized")
}
}
reference to self in the addTarget method inside the closure doesn't seem to create a strong reference cycle.
Can someone explain why?
Also, I noticed that if I remove inheritance from UIView the compiler starts complaining: Use of unresolved identifier 'self'.
Can someone explain this as well, why does it happen in this case and doesn't in the first one?
This is not a retain cycle because self is not what you think it is :)
Properties with initial value are "executed" even before any initializer runs, and for those properties self points to a higher order function of this type:
(TestClass) -> () -> TestClass
So you don't really access the instance, but rather you access a static-like method that does the initialization of all properties that have a default value. This is why you don't have a retain cycle.
addTarget accepts an Any? value for it's first argument, so this violates no type rules so the compiler doesn't complain that you don't pass a NSObject instance there.
Checking the later behaviour - e.g. what happens if the button is added to the UI hierarchy and is tapped, reveals something interesting: the runtime sees that you passed a non-object as a target and sets null values for target and action:

custom class swift setting constraints to superview

I want to create a custom UIView that I can add to any view and have it show a red error message like instagram does in the picture. I am able to do this programmatically in my view controller but want to create a modular class that I can add it into any view where I want to show an error. My question is how can I add constraints to superview in a custom class?
For example how would I do:
topAnchor.constraint(equalTo: view.topAnchor, constant: 0)
leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0)
rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0)
widthAnchor.constraint(equalTo: errorLabel.frame.size.height,0)
In a custom class so that I don't have to do it in my view controller? Can I access whatever superView's constraints so that my view is always located at the top?
class CustomUIAlertView : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Can you give guidance on how to approach this? I feel like I am missing something basic.
I think the best way is to pass the superview as parameter to the init method.
Something like that:
class CustomUIAlertView : UIView {
weak var parent:UIView
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(frame:CGRect, superview:UIView) {
super.init(frame: frame)
self.parent = superview
}
}

swift IBDesignable view not show in storyboard

I want to custom a 5-star UIView,also I want it to be render in storyboard. So I decide to use #IBDesignable and #IBInspectable.The following is my code.
import UIKit
#IBDesignable
class RatingView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
}
func setUpView() {
let imageView = UIImageView(frame:CGRectMake(0, 0, 50,50))
imageView.image = UIImage(named: "star")
addSubview(imageView)
}
}
And then in my storyboard , I pull a UIView into my canvas,and set Custom class to my custom view as RatingView.The compiler starts to compile storyboard file and I just wait for the custom view to be renderd in canvas.Here is the screenshot.
The state is "up to date",but the view has not been renderd.The view is just staying white,what I want to see is the image I add to the parent view.
When I use UILabel instead of UIImageView, the label is renderd in the canvas but not the UIImageView,how can I render my lovely star image in my canvas.(Images.xcassets has star.png file)
use UILabel instead of UIImageView
func setUpView() {
let label = UILabel(frame: CGRectMake(0, 0, 50, 50))
label.text = "text"
addSubview(label)
}
result:
I was trying to do the same exact thing. You need to put the view in a framework. As #Benson Tommy said in the comments take a look at WWDC 2014 session 411.
Here is a link to the session:https://developer.apple.com/videos/wwdc/2014/#411
Here is a link to the transcript of the session: http://asciiwwdc.com/2014/sessions/411