Change IBOutlet button's type - swift

I'm trying to achieve something I'm not sure if it's doable. I have a xib for UITableViewCell that has a UIButton in it with custom class assigned in storyboard i.e MainButtonSuperClass. In UITableViewCell.swift outlet is connected like following,
#IBOutlet weak var button: MainButtonSuperClass!
Many different controllers are configuring this TableViewCell but button's UI will be different for all those controllers as defined in subclass i.e.
final class MainButtonSubClass: MainButtonSuperClass {
override func configure(){//different style of button }
}
How can I achieve this? so far I have tried following,
let button: MainButtonSuperClass = MainButtonSubClass()
button.configure()
cell.button = button
But this doesn't pick the style
EDIT:
//tableview cell
class TableViewCell: UITableViewCell {
#IBOutlet weak var button: MainButtonSuperClass!
}
//Controller A wants default style of superclass (MainButtonSuperClass)
func configureButton(buttonCell: TableViewCell) {
buttonCell.button.configure() //works
}
//Controller B wants different style of subclass (MainButtonSubClass)
func configureButton(buttonCell: TableViewCell) {
let button: MainButtonSuperClass = MainButtonSubClass()
button.configure()
cell.button = button // doesn't work or adopts the style
}
//Custom button's classes
final class MainButtonSuperClass: UIButton {
override func configure(){//styled button }
}
final class MainButtonSubClass: MainButtonSuperClass {
override func configure(){//different style of button }
}

Ok understand, let's try this
class CustomClass: UIButton {
class Type1Class: UIButton {
// Do all the setup here
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class Type2Class: UIButton {
// Do all the setup here
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}
Then you can initialize it with the following:
#IBOutlet weak var button: CustomClass.Type1Class!
Hope it helps!

I found the solution. I will not use IBOutlet but just a property of UIButton that I will change dynamically with factory method. With IBOutlet it's not possible.

Related

How to separate view from viewcontroller using storyboard swift?

Practicing MVC pattern and want to separate view from viewcontroller using storyboard.
in main.storyboard I have a viewcontroller, and there are some uilabels in rootview.
to separate view code from viewcontroller, I selected view from viewcontroller scene , created FruitDetailView class and subclassed it in storyboard identity inspector.
And connected UILabels to FruitDetailView class.
class FruitDetailView: UIView {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var type: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
name.text = ""
type.text = ""
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(_ detail: Fruit) {
name.text = detail.name
type.text = detail.type
}
}
In the viewController , created FruitDetailView() instance.
And in loadview() methods, assigned fruitDetailView instance to view property.
class FruitDetailViewController: UIViewController {
private let fruitDetailView = FruitDetailViewController()
var fruit = Fruit()
override func loadView() {
view = fruitDetailView
fruitDetailView.update(fruit)
}
}
But when I run the app, app crashes with error.
How can I fix this?
Your problem is you want to reach your FruitDetail class created before even its properties being created. Put a breakpoint to the name.text = "" and run and you will see what i mean. To solve this just control your nil value safely :
if let label = name { // by the way consider in future to assign values like nameLabel rather than name
name.text = "put your string value"
}

UIView IBOutlet not changing when change called from a different class

I have an XIB with a UILabel to it. I have referenced the UILabel to a UIView class that I have created. I can change the label using label.text = "hi" when initializing the view. When I try and call a change from another class it doesn't change the UILabel on screen (but if I print label.text it shows as what I set it to). I cannot make the UILabel load the text when initializing as the text could be changed by the user at any time. (switchText() is called from a UITableCell)
2nd class
class Second {
func switchText() {
let first = First()
DispatchQueue.main.async {
first.label.text = "bye"
}
}
}
1st class
class First: UIView {
let kCONTENT_XIB_NAME = "First"
#IBOutlet var label: UILabel!
#IBOutlet var contentView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
Bundle.main.loadNibNamed(kCONTENT_XIB_NAME, owner: self, options: nil)
contentView.fixInView(self)
}
override func awakeFromNib() {
super.awakeFromNib()
}
}
Also, in my XIB I have my UIView hooked up to File's Owner and contentView inside my UIView class. My label outlet goes to file's owner and then to the UIView class where it is declared as label.
You are not really changing the text on your First() class. What your switchText() function does is create another reference of the class named First and then set the text of the label for that new reference.
let first = First()
DispatchQueue.main.async {
first.label.text = "bye"
}
What you can do is make your switchText() function conform to a protocol then call it on your First() class through a delegate.
protocol SecondClassDelegate {
func didSwitchText(editedText: String)
}
class Second {
var delegate: SecondClassDelegate!
func switchText() {
delegate.didSwitchText("bye")
}
}
Now you can add this to your First() class
class First: SecondClassDelegate {
func didSwitchText(editedText: String) {
label.text = editedText
}
}
Just don't forget to set the delegate wherever you're setting your Second() class
let second = Second()
second.delegate = self
I suggest reading about this for a better understanding of delegates. https://www.appcoda.com/swift-delegate/

Adding a ViewController into a UICollectionviewcell

I would like to embed a ViewController inside a UICollectionView cell, I tried to search on the internet but I haven't found anything applicable to my case.
I've done this so far,
this is the View Controller:
class ExploreViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGreen
}
}
This is the collectionViewCell:
class ExploreCollectionViewCell: UICollectionViewCell {
let exploreViewController = ExploreViewController()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(exploreViewController.view)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Basically I'm instantiating a ViewController inside the cell and displaying its view;
I don't think this is actually the best thing to do but honestly I don't know how to implement it in a different way.
The best thing would be to actually embed the viewController inside the cell, do you know how could I do it?

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.

IBOutlet is nil in IBDesignable class

I have a custom view class that is supposed to just contain a label. I have an extension in each target that will provide the text that should be displayed in the view, this way I don't have recreate each controller just to have a different label and background color in it.
My custom code is:
#IBDesignable
class HomeAccentView: UIView {
#IBOutlet weak var nameLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func awakeFromNib() {
super.awakeFromNib()
setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
}
private func setup() {
backgroundColor = UIColor.main
nameLabel.text = String.headerText
nameLabel.textColor = UIColor.white
}
}
I have my nib's file owner as HomeAccentView and the label is connected as an outlet on the storyboard.
But whenever I try to set the text of the label it's always nil. What am I missing or doing wrong?
You did not set class for view. You set only file owner, which means that you told that HomeAccessView is responsible for loading the view, but your code is not showing this. Is there any motive behind this? Otherwise, remove class from file owner and set it for view.