How to access an object inside a ViewController from within a TableViewCell - swift

I am fairly new at coding in swift and I've been trying to find a solution to my problem for the past couple days, and am not able to.
I have a class named userData with various properties:
class UserData {
var name: String
var age: Int
var credits: Int = 1
...
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
Inside the viewController.swift I have declared an object of this class:
...
var user = UserData(name: "testing", age: 5)
...
Inside the same viewController I have a UITableView with a few UITableViewCells. In every cell there is a UIStepper
Using the UIStepper from a cell I want to increment or decrement the property credits of my object user that sits inside the viewController.swift, and do that from within the tableViewCell.swift file
From what I can find, I think I should use a delegate but I can figure out how to implement it.
More information:
Pardon the art work, I am not an artist...
The user has a set amount of ants available to work (this number is a property of my object user). There is also a property for the amount of ants currently working ('ants used' in my screen shot).
At first, the white label on top says '0/5' (Meaning the user has 5 available ants to work but none are currently working).
When the user increments the stepper for 'scavenger ants', the white label on top should say '1/5' (Meaning that there is currently 1 ant working out of 5 that are available).
What I want to do, is that when the user clicks on a stepper, the user's property for the 'ants currently working' increments or decrements appropriately.
Thank you

Set that logic up in the view controllers' cellForRowAt.
When setting up the cell, you can add a function inside your view controller as the target every-time the UIStepper value changes:
// inside cellForRowAtIndexPath:
cell.addTarget(self, action: #selector(doSomething(sender:), for: .valueChanged)
Inside doSomething you set up the logic for updating your model.
func doSomething(sender: UIStepper) {
// do stuff here
}
Edit with an example of the delegate pattern which would be a better solution, for future readers.
First create a protocol:
protocol StepperCellDelegate {
func didChangeValueForStepper(inCell: Cell, whateverInfoYouWantHere:...)
func otherUsefulFunctions(...)
}
In your cell class, set a target/action for when your value is changed:
// inside the cell's initialization (`init(style:reuseIdentifier:)`
self.addTarget(self, action: #selector(valueChanged(sender:), for: .valueChanged)
Your cell also needs a delegate property:
weak var stepperCellDelegate: StepperCellDelegate?
doSomething would look something like this:
func valueChanged(sender: UIStepper) {
stepperCellDelegate?.didChangeValueForStepper(inCell: self, ...)
}
Your ViewController will implement the protocol
extension MyViewController: StepperCellDelegate {
func didChangeValueForStepper(inCell: Cell, whateverInfoYouWantHere:...) {
// implementation here
}
func otherUsefulFunctions(...){
// implementation here
}
}
And inside cellForRowAtIndexPath set itself as the delegate:
// inside cellForRowAtIndexPath:
cell.stepperCellDelegate = self

Delegation is the proper way of handling this.
That UItableviewCell — both appear to be similar —
should expose a protocol with a set of methods
that the UIViewController can implement. Methods are
triggered when you tap on the UIStepper widget.
Best to stick to this methodology, and reuse it everywhere. Makes
things manageable loosely coupled.

Related

How do we return a value of collection view that is inside of tableview cell?

I have a table view cell that has a collection view inside of it, and i wanted to make a static method i can use from outside the class to get the value but i can not do that
what is an alternative way to return size of this collection view inside the table view cell
What I did is i stored a property with collection view height but couldn't return it in func
There are many ways to achieve this. One of the simplest ways it to use protocols.
In your CollectionViewCell but outside of your class, define protocol:
protocol CollectionViewCellDelegate: class {
func sizeOfCollectionView(size: CGSize)
}
In your CollectionViewCell class; define this protocol as a weak variable
weak var delegate: CollectionViewCellDelegate?
In your CollectionViewCell class; where you have the correct size; return it to any observers of this delegate using the following method:
self.delegate?.sizeOfCollectionView(size: bounds.size)
Now this delegate receives sizeOfCollectionView calls and it's parent can observe it.
For example:
In your parent class:
cell.delegate = self
After doing this try to build the project and Xcode will tell you that your class doesn't have the delegate implemented. You can then define the delegate as follows:
extension ParentClass: CollectionViewDelegate {
func sizeOfCollectionView(size: CGSize) {
// You will receive size here
}
}
Here I'm also adding a medium tutorial for a more detailed explanation on this.
Hope these help!

Table with static cells won't scroll when I set delaysContentTouches = false

The problem: HIGHLIGHT vs SCROLLING
My buttons inside the cell where not getting highlighted when I lightly tap on them. I had to tap hard and for a long time to be able to see the tap state of the button.
So I did this in order to set the delaysContentTouches to false (I didn't manage other way to do it) inside viewDidLoad():
for index in tableView.subviews {
if (index.isKindOfClass(UIScrollView)) {
let scrollViewFound = index as! UIScrollView
scrollViewFound.delegate = self
scrollViewFound.delaysContentTouches = false
scrollViewFound.canCancelContentTouches = true
scrollViewFound.scrollEnabled = true
}
}
This way the buttons highlight correctly but then I cannot scroll the table up or down, unless I start dragging from one of the empty cells --> userInteractionEnable = false in the empty cells
What I need:
To be able to highlight the buttons but also to scroll the table.
Is it even possible to have both, scrollable view and highlighted buttons?
What I have tried
I tried calling this function:
func touchesShouldCancelInContentView(view: UIView) -> Bool {
print("touchesShouldCancelInContentView happening---------")
return true
}
Which never gets called. I tried overriding But it gives an error:
Method does not override any method from its superclass
Which is weird, because UITableViewController inherits from UIScrollView. I also tried adding UIScrollViewDelegate to the class definition, but of course it gives another error that this is redundant.
Extra Information
The class is declared like this:
class Settings: UITableViewController, UITextFieldDelegate { ...
The tableView is made of Static Cells
The cells:
Some are empty: where UserInteractionEnable = false
Some have buttons with text field: I want these buttons to get highlighted. UserInteractionEnable = true. The button action is called by .TouchUpInside
Some have labels and a check image: Their action gets called in didSelectRowAtIndexPath which will change the labels colour and check images
Maybe it is relevant to say that when user clicks on any cell didSelectRowAtIndexPath it will
call a function to dismiss the keyboard.
You tried to subclass the wrong class, that's why it doesn't work. You have to subclass the UITableView class itself, and not the UITableViewController.
Can you try the following ?
- First
Subclass the TableView class in order to override the touchesShouldCancelInContentView function.
class UIDraggableTableView: UITableView {
override func touchesShouldCancelInContentView(view: UIView) -> Bool {
if (view.isKindOfClass(UIButton)) {
return true
}
return super.touchesShouldCancelInContentView(view)
}
}
- Second
In your TableViewController class, when viewDidLoad() is called, append the following right after super.viewDidLoad():
self.tableView = DraggableTableView()
This should solve your issue.
Part of this answer was taken from this StackOverflow post.

why pass viewController as variable?

First of all I have to say I am really new to swift and Objective C.I am learning them by myself.
I have a question for this code
I have a delegate in my SettingViewController called "settingsViewControllerFinished" and it pass the whole controller as a variable.
the code like this:
in my SettingViewController.swift
protocol SettingViewControllerDelegate: class {
func settingsViewControllerFinished(settingsViewController: SettingsViewController)
}
#IBAction func close(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
self.delegate?.settingsViewControllerFinished(self)
}
I am confused.What did you mean if you pass the whole controller as a variable?(maybe the question is silly for you)
in my viewController:
func settingsViewControllerFinished(settingsViewController: SettingsViewController)
{
self.brushWidth = settingsViewController.brush
self.opacity = settingsViewController.opacity
self.red = settingsViewController.red
self.green = settingsViewController.green
self.blue = settingsViewController.blue
}
I guess the reason is:I pass everything in SettingViewController to ViewController so that I could use the variables in SettingViewController.
Am I rihgt?
Generally you are correct, yes: passing the SettingViewController back to its delegate enables the original caller to not have to keep a reference to the created and shown SettingViewController since the delegate method sends the relevant information along already.
But there is more: In some cases of delegates this style is useful for something different. Imagine a click handler consisting of a function func somethingGotClicked(sender:YourSenderType). If your class creates multiple instances of YourSenderType and shows them at the same time registering itself as their delegate there would be no way to know which one got clicked if there was no sender parameter. In some func somethingGotClicked() you would not know which one got clicked. That capability is often needed when showing multiple UITableView or UICollectionView is one single view with one single instances set as their delegate.

Changing the value of a variable in another a class using a UIButton Swift 2

I have a View Controller (called BackgroundViewController) which has a few buttons, each of them set the color of the background of a different view, my main view. (just called ViewController, yes I started this project about a month ago, before I knew that I should name it something better). For that I set a class, SoundboardBrain, which I intend to use to hold a lot of the app's logic. Here's the class so far:
var backgroundName = String()
init(){
backgroundName = "Image"}
func changeBackgroundName(background: String){
backgroundName = background}
Now, BackgroundViewController is kind of like a settings pane, where the user could select one of the options and a bullet point appears by the one that he checked. Here's one of the of the buttons:
#IBAction func whiteButton(sender: AnyObject){
whiteBullet.hidden = false
imageBullet.hidden = true
}
//Here I call the changeBackground function I defined in SoundboardBrain
SoundboardBrain.changeBackgroundName("White")
//I then print the result of that and I still get "Image" NO MATTER WHAT!
So all I want to know is how to change a variable initialized in a class with a UIButton or another object of a ViewController.
You should keep the instance of SoundBrain somewhere in variable, or use a singleton. You could be initializing a new SoundBrain instance later.
Singleton is better for main app logic. Example:
class SoundboardBrain {
static let shared = SoundboardBrain()
var backgroundName = "Image"
func changeBackgroundName(background: String) {
backgroundName = background
}
}
SoundboardBrain.shared.backgroundName
// now the property is "Image"
// in UIButton
SoundboardBrain.shared.changeBackgroundName("something")
SoundboardBrain.shared.backgroundName
// now it's "something"
Example was made in Playground, but it doesn't matter.

NSCollectionView selection handling in Swift

Learning with Swift and I've been at this all day with little progress:
Need to know when an item in NSCollectionView is selected. The end goal is to get the item to highlight and to be able to delete it from the collection with the delete key. My NSCollectionView is bound to an ArrayController to get content and send the selection indexes, so looks like I need to be watching the ArrayController for a selection change, but don't see any helpful delegate methods there. The prototype view has a single textfield.
I was following the obj-c examples here and elsewhere (found none in Swift), but a Swift NSCollectionViewItem doesn't have the setSelected method to override. It has a selected property.
How to get informed when an NSCollectionViewItem gets selected in Swift?
The most simple solution is to override the selected property and react for example whenever it is set:
class CollectionSonaViewItem: NSCollectionViewItem {
override var isSelected: Bool {
didSet {
// set background color to indicate selection
self.view.layer?.backgroundColor = (isSelected ? NSColor.blue.cgColor : NSColor.clear.cgColor)
// do more stuff
}
}
From there on you can send a notification or call a function in your collection view class, its delegate or whatever required.