init() inside custom cell class that is delegate and datasource of collectionView - ios

Im really confused trying to init() some stuff inside my custom cell class that i want to be the datasource and delegate of a collectionview.
How do i init() so that i have the data in an array ready to be used by cellForItemAt??
var partArray : [CollectionStruct] = []
init(partArray: [CollectionStruct]) {
super.init(partArray: [CollectionStruct])
innerCollectionView.delegate = self
innerCollectionView.dataSource = self
//innerCollectionView.tag = item
// add some stuff from local sql lite to array
// this is how i normally do this in viewDidLoad
// but cant use that in cell subclass
BuildArray.buildArrayFromQuery(queryForCollection: "Part", delegateSender: "DownloadPack", completeBlock: { (result) in
if result.isEmpty == false {
self.partArray = result
}
})
}
If i just do:
init() {
// stuff for the array
}
I get swift suggesting i include this block:
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

You can't create your own initializers for the UITableViewCell. The only two initializers you can override are one that recieves a frame (init(frame)) and one that recieves a code (init(coder)).
In order to find a solution for your problem, you can a use either a property or a method in which recieves the data array. Either one would be called or set after the cell is created. You make your setup, and afterwards you call reloadData() of the collectionView.
If you decide to use a property, you could do this mentioned logic in the didSet of the property.

Related

Swift: 'super.init' isn't called on all paths before returning from initializer?

I am getting this error on the last brace of a init in a class of mine. The class looks something like the following (I market the spot where error happens):
class RecordingViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
let cameraButton:UIButton?
let camPreview:UIView?
init (cameraButton: UIButton!, camPreview: UIView!) {
self.cameraButton = cameraButton
self.camPreview = camPreview
} //get error here
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//do a bunch of other stuff
}
I have looked here and here for a solution but both seem like solutions that are either really bad or that are too specific to that question, thus they have not work for me.
I was hoping for a solution to my problem done in such a way that it can help me understand why this error is happening.
Since you inherit from UIViewController, you should call super.init right after you set the variables in your init function
When you inherit a class and implement a new init function or override its own init function you should (almost) always call super.init. Let's take your example, you inherited from UIViewController. UIViewController has a few init functions that you can use to initialize a view controller. if you don't call super.init, all the code inside those functions will not get called and possibly the view controller won't get initialized.
Anyway, this piece of code should work for you:
class ViewController: UIViewController {
var button: UIButton?
init(button: UIButton) {
self.button = button
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is what I found on Swift Programming Language:
In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
Hope this can explain that question.

Determine if `UIView` is loaded from XIB or instantiated from code

I use the following code to force a subclass of UIView to load from a XIB file whose name is the actual class name:
class NibView : UIView {
override func awakeAfter(using aDecoder: NSCoder) -> Any? {
guard isRawView() else { return self }
for view in self.subviews {
view.removeFromSuperview()
}
let view = instanceFromNib()
return view
}
func isRawView() -> Bool {
// What here?
}
}
The purpose of the isRawView() method is to determine whether this view has been created from code, or it's been loaded from the corresponding XIB file.
The implementation I've used so far is:
func isRawView() -> Bool {
// A subview created by code (as opposed to being deserialized from a nib)
// has 2 subviews, both implementing the `UILayerSupport` protocol
return
self.subviews.count == 2 &&
self.subviews.flatMap(
{ $0.conforms(to: UILayoutSupport.self) ? $0 : nil }).count == 2
}
which uses a trick to determine if the view is created from code, because in such cases it contains exactly 2 subviews, both implementing the UILayoutSupport protocol.
This works nicely when a NibView subclass is instantiated from code. However it doesn't work if the view is created as part of a view controller in a storyboard (and presumably the same happens for view controllers and views loaded from XIB files).
Long story to explain the reason of my question: is there a way for a UIView to know whether it's been loaded from a XIB file, and possibly the name of that file? Or, otherwise, an alternative way of implementing the isRawView() method, which should:
return false if the view has been deserialized from an associated XIB file (whose name is the class name)
return true otherwise
Make use of the provided init functions.
init(frame:​) -> From code
init(coder:​) -> From nib
Example code:
override init(frame: CGRect) {
super.init(frame: frame)
print("From code")
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
print("From nib")
}
I can print out the class name like this:
print(NSStringFromClass(type(of: self)).components(separatedBy: ".").last ?? "Couldn't get it")
You should be able to use that, maybe with some slight adjustments, to get what you need.

Providing my own initializer for UICollectionViewFlowLayout

I'd like to provide my own initializer for my UICollectionViewFlowLayout subclass, for the purposes of calculating my itemSize property at runtime to scale my cells properly for different screen sizes. I understand that the simplest way to do so, is to adjust the default UICollectionViewFlowLayout from the UICollectionViewController like this:
//in UICollectionViewController
override func viewDidLoad() {
super.viewDidLoad()
let someWidth = ...
let someHeight = ...
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: someWidth, height: someHeight)
}
However, I need to subclass UICollectionViewFlowLayout for the purposes of some custom layout for my UICollectionReusableView, so I thought it would be nice to declare the itemSize property in my layout subclass.
I am trying to make my own initializer, like so:
override init() {
super.init()
itemSize = CGSizeMake(someWidth, someHeight)
}
but I am greeted with the requirement:
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
When I attempt to load this viewController, I'm greeted with the fatalError. Next, I try to simply let the super implementation deal with it:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
I recall various bits of information that is forcing my mind in a state of caution, something along the lines of: If one decides to subclassing something that requires the NSCoding things, one must do something to the variables that I added to the subclass.
Can anyone advise me on this?
-- EDIT --
Is this what you're looking for?
class HmIsThisOK: UICollectionViewFlowLayout {
var myCustomSize: CGSize = CGSizeMake(60.0, 23.5)
override var itemSize: CGSize {
get { return myCustomSize }
set { myCustomSize = newValue }
}
}
or does it just make sense to follow the usual protocol of initializing itemSize in your implementation of prepareForLayout()?
If none of that works for you, it's perfectly safe to do the required init junk and just add your code to the end of it after calling super.init(...). You're not concerned with tying your custom init to the coding/encoding process, which has to do from awakening from the Nib file (usually Main.storyboard). Because the two inits() are required, you usually factor out your custom init code into a helper method.
Finally, If you are indeed awakening this object from the Nib file, you can use an overridden awakeFromNib() function to do custom initialization, without worrying about the init() prickliness.

How to initialize the different properties of Custom UITableViewCell in swift

I was trying to work with custom tableview cells for the tableview in my project. Right now I have got the tableview working using the custom cell class that I have created. I am using XIB for the cell.
But now the problem is that I want to add a gradient layer & an extra label as properties to my cell class and I want to get them initialize. I am writing the code for these properties in the init() like
override init(){
//Code to initialize the properties of cell
.....
super.init()
}
And I have also have the required init() which is like this
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
But its not working its just giving an error at required init().
I know how to get this done in Objective-C but have no idea how to do this in swift. Please give me some pointers on how to get this done.
EDIT : Yeah I did try initializing the properties in the awakeFromNib() where I got error saying I can't assign to property in self. And in init(coder:) also I was unable to initialize the properties.
Thanks in advance :)
Something wrong in your code. I create new project, create custom UITableViewCell via nib, and put such code, and it works fine:
override func awakeFromNib() {
super.awakeFromNib()
self.textLabel.text = "testing cells";
self.detailTextLabel?.text = "text";
}
Did you register cell's nib in table view?
self.tableView.registerNib(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "Cell");
Also did configure right the nib in Interface Builder? Does you change UITableViewCell class to your custom class?

How To Init A UIView That Exists In Interface Builder But Also Requires Init In Code

Here is my view class
class V_TakePhoto:UIView{
var _takePhotoCallback:(iImage)->Void?
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
_takePhotoCallback = nil
}
#IBAction func takePhoto(sender: AnyObject) {
println("Here we go!")
}
func initWithCameraCallback((iImage)->Void)
{
}
}
This class is a UIView subclass. In the interface builder I selected the ViewController class, and then selected its View object. I assigned V_TakePhoto to this view object.
In the ViewController class, which I assigned to C_TakePhoto class, I want to init the V_TakePhoto class.
As you can see, I want it to have a callback variable that it gets passed at run time. However, because the view is already getting initialized from the interface builder, init(coder) is getting called first.
As it stands right now it seems hacky that I need to have 2 init functions. One where interface builder calls it, then again when my ViewController inits the view with its callback. Also I will have a number of variables, and I need to pre-init them in the init(coder) call then RE-init them again when the ViewController calls the 'true' init on the V_PhotoClass. Seems very hacky to me, there must be a clean 'correct' way to do this.
Can you suggest a cleaner way to handle a situation where you have variables and need to init a view despite there being an init(coder) call from the interface builder?
I would suggest creating a function in V_TakePhoto and call it in both V_TakePhoto's init(coder) and ViewController's viewDidLoad(), something like :
In V_TakePhoto :
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
specialInit()
}
func specialInit() {
// some of your view initialization
}
In your View Controller :
#IBOutlet weak var takePhotoView: V_TakePhoto!
override func viewDidLoad() {
super.viewDidLoad()
// this method is called after the view controller has loaded its view hierarchy into memory.
takePhotoView.specialInit() // RE-init
}

Resources