How to access a superview's view controller from a child view - ios

I'm creating a UIView that will have a UITableView inside it. I want to set this UITableView delegate and data source equal to the view controller of its superview.
Code:
import UIKit
class AllTasksView: UIView {
let tableview = UITableView()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func didMoveToSuperview() {
tableview.delegate = "SUPERVIEW.ViewController"
tableview.dataSource = "SUPERVIEW.ViewController"
}
Is it possible to do so like this? and where is it best to override the UITableview methods? I have already done so in my UIViewController.

Due to separation of concerns, you cannot access a UIViewController from a UIView.
Alternatively, you can use delegation to access the UIViewController
class AllTasksView{
weak var delegate: UITableViewDataSource & UITableviewDelegate?{
didSet{
tableView.delegate = newValue
tableView.dataSource = newValue
}
}
}
ADDITIONAL
class CustomVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
//implement tableview methods here
func viewDidLoad(){
super.viewDidLoad()
let allTasksView = AllTasksView()
view(addSubview: allTasksView)
//add some positioning and size constraints here for allTasksView
allTasksView.delegate = self //this would call the didSet and would automatically setup the allTasksView.tableView's delegate and dataSource to this vc
}
}

Related

How to constraint UITableView over UIView in UIView subclass and keep dataSource and delegate methods in UIViewController?

How to constraint UITableView over UIView in UIView subclass and keep dataSource and delegate methods in UIViewController?
Create a var inside the custom view like
weak var delegate:VCName?
Then inside it add the table and set
self.tableView.delegate = delegate
self.tableView.dataSource = delegate
class CustomView:UIView {
weak var delegate:VCName?
init(frame: CGRect,del:VCName) {
super.init(frame: frame)
delegate = del
setup()
}
func setup() {
// add table here with constraints
self.tableView.delegate = delegate
self.tableView.dataSource = delegate
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Make the constraints inside myView and set the delegate and datasource in VC like:
myView.myTableView.delegate = self
myView.myTableView.dataSource = self

IBoutlet Connected From SuperClass UIView Still Nil

I work with Nibs. I have two screens that will use the "same" UIView component with the same behavior. It's not the same component because in each screen i placed a UIView and made the same configuration, as show on the image.
To solve this and prevent replicate the same code in other classes i wrote one class, that is a UIView subclass, with all the functions that i need.
After that i made my custom class as superclass of these UIView components to inherit the IBOutlets and all the functions.
My custom class is not defined in a Nib, is only a .swift class.
I made all the necessary connections but at run time the IBOutlets is Nil.
The code of my custom class:
class FeelingGuideView: UIView {
#IBOutlet weak var firstScreen: UILabel!
#IBOutlet weak var secondScreen: UILabel!
#IBOutlet weak var thirdScreen: UILabel!
#IBOutlet weak var fourthScreen: UILabel!
private var labelsToManage: [UILabel] = []
private var willShow: Int!
private var didShow: Int!
override init(frame: CGRect) {
super.init(frame: frame)
self.initLabelManagement()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initLabelManagement()
}
private func initLabelManagement() {
self.initLabelVector()
self.willShow = 0
self.didShow = 0
self.setupLabelToShow(label: labelsToManage[0])
self.setupLabels()
}
private func initLabelVector() {
self.labelsToManage.append(self.firstScreen)
self.labelsToManage.append(self.secondScreen)
self.labelsToManage.append(self.thirdScreen)
self.labelsToManage.append(self.fourthScreen)
}
private func setupLabels() {
for label in labelsToManage {
label.layer.borderWidth = 2.0
label.layer.borderColor = UIColor(hex: "1A8BFB").cgColor
}
}
func willShowFeelCell(at index: Int) {
self.willShow = index
if willShow > didShow {
self.setupLabelToShow(label: labelsToManage[willShow])
}
else if willShow < didShow {
for i in didShow ... willShow + 1 {
let label = labelsToManage[i]
self.setupLabelToHide(label: label)
}
}
}
private func setupLabelToShow(label: UILabel) {
label.textColor = UIColor.white
label.backgroundColor = UIColor(hex: "1A8BFB")
}
private func setupLabelToHide(label: UILabel) {
label.textColor = UIColor(hex: "1A8BFB")
label.backgroundColor = UIColor.white
}
}
I found this question similar to mine: Custom UIView from nib inside another UIViewController's nib - IBOutlets are nil
But my UIView is not in a nib.
EDIT:
I overrided the awakeFromNib but it neither enter the method.
More explanation:
My custom class is only superClass of this component:
Which i replicate on two screens.
One screen is a UITableViewCell and the another a UIViewController.
It's all about to manage the behavior of the labels depending on the screen that is showing at the moment on the UICollectionView
When the initLabelVector() function is called at the required init?(coder aDecoder:) it arrises a unwrap error:
The error when try to open the View:
Cannot show the error with the UITableViewCell because it is called at the beginning of the app and don't appear nothing. To show the error with the screen i needed to remove the call of the UITableViewCell.
The UITableViewCell is registered first with the tableView.register(nib:) and after using the tableView.dequeueReusebleCell.
The UIViewController is called from a menu class that way:
startNavigation = UINavigationController(rootViewController: FellingScreenViewController())
appDelegate.centerContainer?.setCenterView(startNavigation, withCloseAnimation: true, completion: nil)
The problem is this code:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initLabelManagement()
}
The trouble is that init(coder:) is too soon to speak of the view's outlets, which is what initLabelManagement does; the outlets are not hooked up yet. Put this instead:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.initLabelManagement()
}
How I arrived at this answer:
As a test, I tried this:
class MyView : UIView {
#IBOutlet var mySubview : UIView!
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
print(#function, self.mySubview)
}
override func awakeFromNib() {
super.awakeFromNib()
print(#function, self.mySubview)
}
}
Here's the output:
init(coder:) nil
awakeFromNib() <UIView: 0x7fc17ef05b70 ... >
What this proves:
init(coder:) is too soon; the outlet is not hooked up yet
awakeFromNib is not too soon; the outlet is hooked up
awakeFromNib is called, despite your claim to the contrary
When the init() from my custom class is called the IBOutlets are not hooked up yet.
So, I created a reference on the parent view and called the iniLabelManagement() from the viewDidLoad() method and everything worked.
Thank you matt, for the help and patience!

Custom UIView without Storyboard

Now I'm practicing build IOS app without using storyboard , but I have a problem want to solve , I created a custom UIView called BannerView and added a background(UIView) and a title(UILabel) , and called this BannerView in the MainVC , but run this app , it crashes at the function setupSubviews() and I don't know why.
import UIKit
import SnapKit
class BannerView: UIView {
var background: UIView!
var title: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupSubviews()
}
func setupSubviews() {
background.backgroundColor = .gray
title.text = "INHEART"
self.addSubview(background)
self.addSubview(title)
}
override func layoutSubviews() {
background.snp.makeConstraints { make in
make.width.equalTo(ScreenWidth)
make.height.equalTo(BannerHeight)
make.left.top.right.equalTo(0)
}
title.snp.makeConstraints { make in
make.width.equalTo(100)
make.center.equalTo(background.snp.center)
}
}
}
class MainVC: UIViewController {
var bannerView:BannerView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView = BannerView(frame: CGRect.zero)
view.addSubview(bannerView)
}
}
Your properties do not appear to be initialised
var background: UIView!
var title: UILabel!
You should initialize these in your init method
self.background = UIView()
self.title = UILabel()
If you use force unwrapping on a class property you must initialize in the init method. XCode should be complaining to you about this and your error message should show a similar error
You are not initialised the background view please initialised them
self.background = UIView()
self.title = UILabel()
and if you want to create custom view by use of xib the follow them Custum View
You must have to initialised the self.background = UIView() and self.title = UILabel() first.
You can initalised them in setupSubviews() function before the set/assign values to them.

Custom UIScrollView class not initializing IBOutlets

I've recently dived into the world of Swift 3 and Xcode 8 and have been working on a simple game to learn as much as possible but I have run into an issue where my custom class is crashing when I segue into it because the IBOutlets which I dragged into it are nil.
I am using StoryBoard and AutoLayout and have dragged several fields into my class statistics:UIScrollView from the storyboard.
class StatisticsView: UIScrollView, sendLabelDelegate {
#IBOutlet weak var GamesWonNum: UILabel!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.GamesWonNum = aDecoder.decodeObject(forKey: "GamesWonNum") as! UILabel!
}
override func encode(with aCoder: NSCoder) {
aCoder.encode(self.GamesWonNum, forKey: "GamesWonNum")
}
func setLabel(data: String) {
let temp = Int(data)
setNumber(num: temp!)
}
override func awakeFromNib() { }
override init(frame: CGRect) {
super.init(frame: frame)
}
func setNumber(num: Int){
GamesWonNum.text = String(num)
}
}
I have made sure that the statistics class is set as the identifier for the storyboard scrollview. I've done a lot of research into the topic, and I couldn't find a concrete answer on exactly how this works. I found in another post that calling the function awakefromNib() will fix the issue and it has, but I want to understand what awakefromNib() does and why I cannot simply init() the view.
My view Controller hierarchy is as follows:
MainMenuVC | --Segue--> SettingsVC
MainMenyVC | --Segue--> StatisticsVC
class StatisticsVC: UIViewController, sendLabelDelegate{
#IBOutlet weak var StatsScrollView: UIScrollView!
var settingsLabel:String = ""
override func viewDidLoad() {
super.viewDidLoad()
if UIScreen.main.bounds.height < 600 {
StatsScrollView.contentSize.height = 700
print("screensizeif: \(UIScreen.main.bounds.height), \(StatsScrollView.contentSize.height)")
}else{
StatsScrollView.contentSize.height = UIScreen.main.bounds.height
print("screensizeelse: \(UIScreen.main.bounds.height), \(StatsScrollView.contentSize.height)")
}
StatsScrollView.contentSize.width = UIScreen.main.bounds.width
StatsScrollView.isUserInteractionEnabled = true
}
func setLabel(data: String) {
settingsLabel = data
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
}
My understanding is that the viewController will init all of its views before going into the viewdidLoad() function, so why is awakefromNib() necessary and what exactly should I define inside of it. Further, why can't I initialize the class inside init()? If I try to set the GamesWon Outlet inside init(), the outlet is nil.
Its a bit difficult for me to wrap my head around the relationship that exists here between a view controller, the custom scrollview I have, and the outlets being nil. If anyone could clarify I would very much appreciate it.
Thank you!

Passing data between custom view and view controller

I've got a custom UIControl (TestControl) and want to pass a simple string to a label on the main view. The UIControl (TestControl) sits inside a UIView (CustomView) which has been placed on the storyboard using a view which has got the custom class (CustomView).
What would be a simple implementation that would take care of that?
I noticed that the CustomView is called before the viewDidLoad() in the ViewController.
ViewController.swift
import UIKit
#IBOutlet var someLabel: UILabel!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
TestControl.swift
import UIKit
class TestControl: UIControl {
// Initializer
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.blackColor()
//this is where it would be good if a string could be passed to a label
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
CustomView.swift
import UIKit
#IBDesignable class CustomView: UIView{
#if TARGET_INTERFACE_BUILDER
override func willMoveToSuperview(newSuperview: UIView?) {
let testing: TestControl = TestControl(frame: self.bounds)
self.addSubview(testing)
}
#else
override func awakeFromNib() {
super.awakeFromNib()
let testing: TestControl = TestControl(frame: self.bounds)
self.addSubview(testing)
}
#endif
}
Since you have a control in a storyboard, all you should have to do is to hook up the the didChange event handler to an IBAction in your view controller. This can then interpret this action to do what you want.

Resources