moving data between classes in swift 4 - ios

I'm new to swift and am making an graphing app with two views where one has a text field to enter data and the other has a view to display the data. I've got the data as two arrays of doubles in my ViewController class, but I can't move the data to the class of UIView where I want to draw to the view it because it does not inherit the arrays. I've tried accessor methods but that hasn't changed anything.
Here is the class that contains xValues and yValues
class ViewController: UIViewController {
#IBOutlet var DataEntry: UITextView!
var xValues = Array<Double>()
var yValues = Array<Double>()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//the xValues and yValues arrays are filled when the view changes on the press of a button
}
public func getXCord() -> Array<Double>{
return xValues
}
public func getYCord() -> Array<Double>{
return yValues
}
}
Here is the class I want them delivered to. I'm getting an error when initializing xCords and yCords to ViewController.getXCord() and ViewController.getYCord() respectively.
class GraphView: UIView{
var points: [Points] = []
var xCords: Array<Double> = ViewController.getXCord()
var yCords: Array<Double> = ViewController.getYCord()
var position = CGPoint(x: 0,y: 0)
}

You are doing this the complete opposite way.
In the MVC pattern, the view, your GraphView class, should never directly talk to the controller. Instead, the view should use a delegate and/or a datasource to communicate with the controller.
Your view should have a GraphViewDatasource:
protocol GraphViewDatasource : class {
func xValues(inGraph: GraphView) -> [Double]
func yValues(inGraph: GraphView) -> [Double]
}
// in GraphView
weak var datasource: GraphViewDatasource?
func reloadData() {
guard let datasource = self.datasource else { return }
xValues = datasource.xValues(inGraph: self)
yValues = datasource.yValues(inGraph: self)
// redraw the graph...
}
Your controller should implement GraphViewDatasource:
class ViewController: UIViewController, GraphViewDatasource {
func xValues(inGraph: GraphView) -> [Double] { return self.xValues }
func yValues(inGraph: GraphView) -> [Double] { return self.yValues }
}
and set self as the data source of the graph view:
let graph = GraphView(frame ...)
self.view.addSubView(graph)
graph.datasource = self
graph.reloadData()

You need to pass xCoords and yCoords to GraphView from ViewController.
First, initialize xCoords and yCoords with empty array:
class GraphView: UIView{
var points: [Points] = []
var xCords: Array<Double> = []
var yCords: Array<Double> = []
var position = CGPoint(x: 0,y: 0)
}
Than pass it from ViewController:
class ViewContoller: UIViewController {
#IBOutlet var graphView: GraphView!
override func viewDidLoad() {
super.viewDidLoad()
graphView.xCoords = self.xCoords
graphView.yCoords = self.yCoords
}
}

Related

delegate method not getting called with UITabBarController

In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.

Passing array of objects between views using the same tab bar controller, Swift 3

Following the tutorial to pass data between views using the same tab bar controller: (add "https//:" in front, need 10 reputation to post 2 links) makeapppie.com/2015/02/04/swift-swift-tutorials-passing-data-in-tab-bar-controllers/
It works perfectly when I am only passing a single object, but not when I am trying to pass an array of the same object.
I've created a simple app to illustrate my problem clearer.
https://i.stack.imgur.com/C4APn.png
The object
class Item: NSObject {
}
The custom tab bar controller
class CustomTabBarController: UITabBarController {
var addedItemArray = [Item]()
var addedItem = Item()
}
The first view controller
class FirstViewController: UIViewController {
var addedItem = Item()
var addedItemArray = [Item]()
#IBOutlet var ItemLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let tbc = tabBarController as! CustomTabBarController
addedItem = tbc.addedItem
addedItemArray = tbc.addedItemArray
//creates the first item in the array
addedItemArray.append(Item())
}
override func viewWillAppear(_ animated: Bool) {
print("addedItem:\(addedItem)")
print("addedItemArray:\(addedItemArray)")
}
#IBAction func addItem() {
//adds additional item to addedItemArray
addedItemArray.append(Item())
print(addedItemArray)
}
}
The second view controller
class SecondViewController: UIViewController {
var addedItem = Item()
var addedItemArray = [Item]()
#IBOutlet var ItemLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let tbc = tabBarController as! CustomTabBarController
addedItem = tbc.addedItem
addedItemArray = tbc.addedItemArray
}
override func viewWillAppear(_ animated: Bool) {
print("addedItem:\(addedItem)")
print("addedItemArray:\(addedItemArray)")
}
}
When I run the code, I get the following in the console, which is the intended output:
addedItem:<Tab_bar_test.Item: 0x60000001d020>
addedItemArray:[<Tab_bar_test.Item: 0x60800001ce20>]
However, when I click on the second tab, I get this:
addedItem:<Tab_bar_test.Item: 0x60000001d020>
addedItemArray:[]
So for some reason, the program passes a single object, but not the array of object.
I'd like to know why, and how to fix this using best practices of Swift 3. Thanks in advance.
try to use a shared array using singleton or a static class
ex:
class Global : NSObject {
static let sharedInstance = Global()
var addedItemArray = [Item]()
}
you can call it in any view you want
first view
class FirstViewController: UIViewController {
var addedItem = Item()
var addedItemArray = [Item]()
#IBOutlet var ItemLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let tbc = tabBarController as! CustomTabBarController
addedItem = tbc.addedItem
addedItemArray = Global.addedItemArray
//creates the first item in the array
Global.addedItemArray.append(Item())
}
override func viewWillAppear(_ animated: Bool) {
print("addedItemArray:\(Global.addedItemArray)")
}
#IBAction func addItem() {
//adds additional item to addedItemArray
Global.addedItemArray.append(Item())
print(Global.addedItemArray)
}
to call the array from any view just call it like this Global.addedItemArray

Redifine a parent properties as controller into a child properties in swift

I have 2 different containerView controllers with similar characteristics. So I decide to create a superclass controller in the storyboard and in code to manage these containerView
class ContainerController: UIViewController {
#IBOutlet weak var containerView: UIView!
weak var listController: UIViewController?
weak var detailController: UIViewController?
let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom
override func viewDidLoad() {
super.viewDidLoad()
activateListController()
addListController()
}
func activateListController(){}
func addListController(){
self.addChildViewController(listController!)
listController?.view.frame = defineChildSize()
self.containerView.addSubview((listController?.view)!)
listController?.didMove(toParentViewController: self)
}
...
}
So, in the child controller, I need to redefine activateListController() with the specific ViewController
class ContainerViewController: ContainerController, ReactionViewDelegate {
var selectedProduct = String()
var selectedFunction = String()
override func viewDidLoad() {
super.viewDidLoad()
}
override func activateListController(){
self.listController = listController as! ReactionViewController
self.listController = self.storyboard?.instantiateViewController(withIdentifier: "ReactionController") as! ReactionViewController?
self.listController.selectedFunction = self.selectedFunction
self.listController?.selectedProduct = self.selectedProduct
self.listController?.delegate = self
}}
But I have an error with ReactionViewController properties: "value of type viewcontroller has no member selectedFunction". The parent properties is not redifined into a child properties
I also try something like that
class ContainerViewController: ContainerController, ReactionViewDelegate {
var reactionViewController: ReactionViewController?
var mechanismViewController: MechanismViewController?
override func viewDidLoad() {
listController = reactionViewController
detailController = mechanismViewController
super.viewDidLoad()
}
and defining activateListController() with reactionViewController, but I had a nil exception on the method addListController() of the superclass
func addListController(){
self.addChildViewController(listController!) //nil exception
So, how can I well manage inherit with my containerview controller?
Thank
Why don't you do something like this:
override func activateListController(){
let newListController = storyboard?.instantiateViewController(withIdentifier: "ReactionController") as! ReactionViewController?
newListController.selectedFunction = selectedFunction
newListController.selectedProduct = selectedProduct
newListController.delegate = self
listController = newListController
}

Swift objects won't update after delegate called

I want a stepper and label to reset to zero after my variable in another class is also reset. The variables reset but the stepper and label do not even after using a delegate.
View Controller:
class ViewController: UIViewController, CircleViewDelegate {
var colors = CircleView()
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var redStepper: UIStepper!
#IBOutlet weak var redValue: UILabel!
#IBAction func stepperChange(sender: UIStepper)
{
circleView1.redd1 = Int(redStepper.value);
redValue.text = Int(sender.value).description;
}
func updateRedStepperValue(value: Double) {
redStepper.value = value
redValue.text = Int(colors.redd1.value).description;
}
override func viewDidLoad() {
super.viewDidLoad()
colors.delegate = self
}
}
CircleView:
protocol CircleViewDelegate
{
func updateRedStepperValue(value: Double)
func updateGreenStepperValue(value: Double)
func updateBlueStepperValue(value: Double)
}
class CircleView: UIView
{
var delegate: CircleViewDelegate?
var redd1 = 0
func updateValues()
{
if(redd1==Int(red1))
{
redd1=0;
delegate?.updateRedStepperValue(0.0)//
}
}
}
The problem is that your making a brand new instance of your CircleView.
let cycle = CircleView()
You need to set your delegate to your current working instance.
To do so, you should replace your assignment in your viewDidLoad with the following:
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication().delegate! as! AppDelegate
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { vc in
if let cont = vc as? CircleView {
cont.delegate = self
}
}
}
}
Here's an article with project files.

Swift: How to use property observers with delegation

I'm trying to use delegation and property observers together to know when a property changes. I setup the protocol but I'm not sure how to use property observers.
I have a class called GridView that is being added to DetailViewController. GridView has an array of ints called rowValues. I would like to observe rowValues from DetailViewController.
GridView.swift
protocol gridViewDelegate {
func rowValueChanged(value: [Int])
}
class GridView: UIView {
var rowValues = [0,0,0,0,0]
var delegate: gridViewDelegate?
func updateRowValue() {
rowValues[0] = 1
}
}
DetailViewController.swift
class DetailViewController: UIViewController, gridViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
var grid = GridView(frame: view.frame)
grid.delegate = self
view.addSubview(grid)
}
func rowValueChanged(value: [Int]) {
println(value)
}
}
Probably this is the syntax you are looking for:
class GridView: UIView {
var rowValues: [Int] = [0,0,0,0,0] {
didSet {
if let theDelegate = self.delegate {
theDelegate.rowValueChanged(rowValues)
}
}
}
var delegate: gridViewDelegate?
}

Resources