Subclass UICollectionViewCell in Swift 1.2 - swift

Given the following UICollectionViewCell subclass:
class MJCollectionViewCell: UICollectionViewCell {
var MJImageView: UIImageView!
var image = UIImage()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: - Setters
func setImage(image: UIImage) {
self.MJImageView.image = image
}
}
I receive the following error on func setImage(image: UIImage):
Method 'setImage' redeclares Objective-C method 'setImage:'

You have to rename the methond setImage(image: UIImage), because there is already a method with this name declared in the Objective-C's UICollectionViewCell class.

Related

how to customize a label when is not in viewdidload?

I have those variables in collectionviewcell file and I want to change label background and cornerradius but that file doesnt have a viewdidload how can I do that . Thanks for helps.
UIView subclasses do not have a viewDidLoad method, instead you use the view's initializer:
class MyCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
layoutUI()
}
private func layoutUI() {
label.layer.cornerRadius = 5
label.backgroundColor = .blue
}
}

Two classes to use the same function to create custom view - Swift

I am kind of new to Swift and I can't figure this out. I have two classes where I need to use the same function to set up a custom UIStackVIew (Rating Control that shows rating stars). Each class has a variable called value that needs to be passed inside the function. I don't want to be duplicating the same code of the setUpStackView function inside each class. I have the following code:
class Class1: UIStackView {
var variable1 = "value1"
override init(frame: CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
setUpStackView(value: variable1)
}
class Class2: UIStackView {
var variable2 = "value2"
override init(frame: CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
setUpStackView(value: variable2)
}
extension Class1 {
func setUpStackView(value: String){
//code to set UIStackView rating control and to use the variable value
}
}
How can I implement the extension for the Class2? I am stuck with this. Any help would be greatly appreciated!
One solution can be moved common code to protocol where you can abstract out:
protocol BaseStackView {
var variable :String { get set }
}
class Class1: UIStackView,BaseStackView {
var variable = "value1"
override init(frame: CGRect){
super.init(frame: frame)
self.setUpStackView(value: variable)
}
required init(coder: NSCoder) {
super.init(coder: coder)
self.setUpStackView(value: variable)
}
}
class Class2: UIStackView,BaseStackView {
var variable = "value2"
override init(frame: CGRect){
super.init(frame: frame)
self.setUpStackView(value: variable)
}
required init(coder: NSCoder) {
super.init(coder: coder)
self.setUpStackView(value: variable)
}
}
extension UIStackView {
func setUpStackView(value: String) {
//Your setup here
}
}
You have many options.
You can make Class2 inherit from Class1:
class Class2: Class1 {
var value = "value2" //You have access to Class1's value, so you can change it here
setUpStackView(value: value) //But there's a problem here
}
But you can't just call a function when you're in the middle of a class declaration.
But you can do this in the initializer:
class Class2: Class1 {
override init(frame: CGRect) {
super.init(frame: frame)
value = "value2"
setUpStackView(value: value)
}
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
value = "value2"
setUpStackView(value: value)
}
}
The above is kind of jank because you're referencing class properties within its own initializer.
Here's another solution:
You can make your setup function a function extension of UIStackView:
extension UIStackView {
func setUpStackView(value: String) {
//Your setup here
self.someProperty = value //self is referring to the stackview itself
}
}
Another option is to create a static function.
extension Class1 {
static func setUpStackView(stackVw: UIStackView, value: String) {
stackVw.someProperty = value
//Doing it like this still makes this function "belong" to Class1
//It also makes it so anyone can set up their stack view like
//this because they have to pass their stack view in here
}
}
Usage would be like this regardless of what class you're in and what inheritance hierarchy you have:
override init(frame: CGRect) {
super.init(frame: frame)
Class1.setUpStackVw(stackVw: self, value: self.variable)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Class1.setUpStackVw(stackVw: self, value: self.variable)
}
Yet another option is to create a protocol from which both Class1 and Class2 conform to.
protocol StackVwWithSpecialSetUp where Self: UIStackView {
var value: String {get}
}
extension StackVwWithSpecialSetUp {
func setUpStackView()
{
self.someProperty = self.value
}
}
And then you'd have your class conform to it
class Class1Or2: UIStackView, StackVwWithSpecialSetUp {
var value: String = "blah" //Compiler will force you to implement this
override init(frame: CGRect) {
super.init(frame: frame)
//Now you can use this
setUpStackVw()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//You can use this here too
setUpStackVw()
}
}
Perhaps the way I'd do it is to just eliminate the need for the value property altogether:
class Class1: UIStackView {
override init(frame: CGRect) {
super.init(frame: frame)
setUpStackVw()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpStackVw()
}
func setUpStackVw()
{
self.accessibilityHint = "value1"
}
}
//////////
class Class2: Class1 {
override func setUpStackVw()
{
self.someProperty = "value2"
}
}

Subclass of UIView doesn't invoke didSet

I have a subclass of UIView named BaseView. In subclass of BaseView I create didSet with some code. In UIViewController I init this subclass of BaseView and he doesn't invoke his didSet
BaseView code:
class BaseView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() { }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Subclass of BaseView code:
class DetailProductView: BaseView {
var product: Product? {
didSet {
productImage.image = UIImage(named: (product?.productImageName)!)
productTitle.text = product?.title
productCompositionLabel.text = product?.description
productPriceLabel.text = "₽" + product!.productPrice!.stringValue
productWeightLabel.text = product!.productWeight!.stringValue + "г."
}
}
UIViewController code:
class DetailProductController: UIViewController {
var product: Product?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let productView = DetailProductView(frame: self.view.bounds)
view.addSubview(productView)
view.layoutSubviews()
}
}
Everything is correct. You created instance of DetailProductView, but you never set any value to it’s product property. Thus didSet was never called (cause you didn’t set anything).
If you want it to be called you should set any value to this property.

What argument do I use when calling draw() function?

I want to draw a line programmatically in swift. I have working code for it (I think), but calling the function requires a CGRect argument. And I'm unsure what code to write there.
The draw() class and function looks like this:
class LineView : UIView {
override func draw(_ rect: CGRect) {
var aPath = UIBezierPath()
aPath.move(to: CGPoint(x:2, y:2))
aPath.addLine(to: CGPoint(x:6, y:6))
aPath.close()
}
}
Now calling it from the main ViewDidLoad it would look like this:
var line = LineView()
line.draw(MISSING ARGUMENT)
But I have no idea what argument I'm supposed to pass. Nothing of the CGRect is used in the function, so I'm not even sure of its purpose.
UPDATE
In main I create the object like this:
class ViewController: UIViewController {
#IBOutlet weak var gameBoard: UIView!
override func viewDidLoad() {
super.viewDidLoad()
var line = LineView()
gameBoard.addSubview(line)
}
}
And my draw class looks like this:
import UIKit
class LineView : UIView {
override init(frame: CGRect) {
super.init(frame: frame) //super = DO REGULAR INIT STUFF FOR UIView
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
var aPath = UIBezierPath()
aPath.move(to: CGPoint(x:2, y:2))
aPath.addLine(to: CGPoint(x:6, y:6))
aPath.close()
UIColor.red.set()
aPath.stroke()
aPath.fill()
setNeedsDisplay()
}
}
Got it working with:
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Don't call draw(rect: yourself. Never do.
It's implicitly called once by the framework after the view was initialized.
If you want to redraw the view call setNeedsDisplay() on the view.

Generate Value for Class Property

I'm trying to subclass UILabel and automatically generate a value for one of the properties, so that I can initialize with some values, and have others randomly generated.
For example, here I'd want to be able to create a new MyLabel, pass in a value for labelString and and have the value for labelNumber generated randomly.
class MyLabel: UILabel {
var labelString : String!
var labelNumber : Int!
init(frame: CGRect, labelString: String) {
self.labelString = labelString
self.labelNumber = createNumber()
super.init(frame: frame)
}
func createNumber() -> Int {
return Int(arc4random())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I get the error "Use of 'self' in method call before super.init initializes self." Is there a way to fix this, or a better pattern to be using?
Many thanks for any help!
You can't call instance methods from init until everything has been initialized. One way to handle it is to make createNumber a static function which doesn't require the class to be initialized to be called:
class MyLabel: UILabel {
var labelString : String!
var labelNumber : Int!
init(frame: CGRect, labelString: String) {
self.labelString = labelString
self.labelNumber = MyLabel.createNumber()
super.init(frame: frame)
}
static func createNumber() -> Int {
return Int(arc4random())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Another way to handle it is to move the initialization of labelNumber after the call to super.init. This works because labelNumber is an Optional and thus is initially given the value of nil which meets the requirement that all properties of the subclass MyLabel are initialized before calling the init of the superclass.
class MyLabel: UILabel {
var labelString : String!
var labelNumber : Int!
init(frame: CGRect, labelString: String) {
self.labelString = labelString
super.init(frame: frame)
self.labelNumber = createNumber()
}
func createNumber() -> Int {
return Int(arc4random())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}