Memory usage increases with every segue to modal vc - ios

I have two view controllers, one is the timeline the second one is for the creation. In that second view controller I have a sub view. This sub view is an SKView. Now every time I segue to it, it increases the memory usage by 2mb (on a real device), but the memory usage stays the same when I unwind it.
So it is like this: I start with a usage of 12mb, then it gets 14-15mb. After the unwind it stays around 14-15mb. After the second segue to it, it becomes 17mb... and so on.
This is the code used in the timeline controller:
#IBAction func createButtonAct(sender: AnyObject) {
self.performSegueWithIdentifier("create", sender: self)
}
#IBAction func unwindFromCreation(segue: UIStoryboardSegue) {
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "create"{
let vc = segue.destinationViewController as! CreateViewController
if vc.currCountry == nil || vc.currCountry != self.currCountry{
vc.currCountry = self.currCountry
}
}
}
And this is the code in the create View Controller:
class CreateViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var bubbleView: SKView!
#IBOutlet var arrow: UIButton!
var ref: Firebase!
let categories = CatsAndColors.categories
#IBOutlet var doneButton: UIButton!
#IBOutlet var titleField: UITextField!
#IBOutlet var descriptionView: KMPlaceholderTextView!
var choosedCat: String!
var selectedCats: NSMutableArray!
var currCountry:String!
var tap: UITapGestureRecognizer!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
UIApplication.sharedApplication().statusBarStyle = .Default
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.arrow.transform = CGAffineTransformMakeRotation(3.14159)
})
titleField.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
titleField.addTarget(self, action: "textFieldDidBegin:", forControlEvents: UIControlEvents.EditingDidBegin)
titleField.addTarget(self, action: "textFieldDidEnd:", forControlEvents: UIControlEvents.EditingDidEnd)
// the targets get removed in viewWillDisappear
selectedCats = NSMutableArray()
}
override func viewDidLoad() {
super.viewDidLoad()
ref = Firebase(url: "https://blabber2.firebaseio.com")
self.doneButton.enabled = false
doneButton.setBackgroundImage(UIImage(named: "Done button inactive"), forState: .Disabled)
doneButton.setTitleColor(UIColor(netHex: 0xF6F6F6), forState: .Disabled)
doneButton.setTitleColor(UIColor.whiteColor(), forState: .Normal)
self.setupBubbles()
self.descriptionView.delegate = self
}
func setupBubbles(){
let floatingCollectionScene = ChooseBubblesScene(size: bubbleView.bounds.size)
// floatingCollectionScene.scaleMode = .AspectFit
/*let statusBarHeight = CGRectGetHeight(UIApplication.sharedApplication().statusBarFrame)
let titleLabelHeight = CGRectGetHeight(self.tibleLabel.frame)*/
bubbleView.presentScene(floatingCollectionScene)
for (category, color) in categories {
let node = ChooseBubbleNode.instantiate()
node!.vc = self
node!.fillColor = SKColor(netHex: color)
node!.strokeColor = SKColor(netHex: color)
node!.labelNode.text = category
floatingCollectionScene.addChild(node!)
}
}
...
And the catsAndColors struct looks like this:
struct CatsAndColors{
static var categories = ["Crime":0x5F5068, "Travel":0xFBCB43, "Religion":0xE55555, "Tech":0xAF3151, "Economy":0x955BA5, "Games":0xE76851, "Climate":0x6ED79A, "Books":0xE54242, "History":0x287572, "Clothes":0x515151, "Sports":0x4AB3A7, "Food":0xD87171, "Politics":0x5FA6D6, "Music":0xDD2E63, "Tv-shows":0x77A7FB]
}

Maybe you have created some sort of retain cycle between your view controllers.
If both view controllers hold a reference to each other, then try declaring one of the references as weak.
For more information on the topic read Resolving Strong Reference Cycles Between Class Instances.

I solved the problem, it was strong reference in the sknode file.
Thank you for your answers.

Related

Why isn't the score showing up after segue on different view controller?

Iv created a quiz app, that tracks the user's score till the user gets to the end. There is an if statement - when it reaches the end of the quiz, a results View controller pops up. My Question is I want to get the same score value presented at the end on the resultsViewContoller. I have connected it via segue and the identifier has been placed. The app runs but when i get to the end the segue opens up the page but the score doesnt change?
How can i solve this problem?
The user finishes the app. I call the segue method and the screen comes up.
Results View Controller
import UIKit
class ResultsViewController: UIViewController {
var fruitness = Fruitness()
#IBOutlet weak var finalScoreLabel: UILabel!
var finalScore: String!
override func prepare(for: UIStoryboardSegue, sender: Any?) {
func viewWillAppear(_ animated: Bool) {
finalScoreLabel.text = finalScore
if finalScore != nil {
finalScoreLabel.text = "FINALSCORE:\(String(describing: Int(finalScore!)))"
}
}
}
}
Game View Controller
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var progressBar: UIProgressView!
#IBOutlet private weak var fruitLabel: UILabel!
#IBOutlet private weak var scoreLabel: UILabel!
#IBOutlet weak var replayGame: UIButton!
#IBOutlet weak var optionButton0: UIButton!
#IBOutlet weak var optionButton1: UIButton!
#IBOutlet weak var optionButton2: UIButton!
#IBOutlet weak var optionButton3: UIButton!
#IBOutlet weak var optionButton4: UIButton!
#IBOutlet weak var optionButton5: UIButton!
#IBOutlet weak var optionButton6: UIButton!
#IBOutlet weak var optionButton7: UIButton!
#IBOutlet weak var optionButton8: UIButton!
#IBOutlet private var fruitButtons: [UIButton]!
var score = 0
var fruitness = Fruitness()
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
#IBAction func replayGame(_ sender: UIButton) {
fruitness.restartGame() //Calling restart
updateUI()
}
#IBAction func touchButton(_ sender: UIButton) {
let userAnswer = sender.currentTitle!
//The userGotItRight is = to the checkAnswer function which goes through the fruitOptions array to make sure the answer is corrct. T/F
let userGotItRight = checkAnswer(userAnswer: userAnswer)
if userGotItRight {
//Depending if the user got the answer correct the button turns green/red
sender.backgroundColor = UIColor.green
} else {
sender.backgroundColor = UIColor.red
}
nextFruit() //Calling the next Fruit untill all the fruit items have been displayed.
//This timer is responsible for the UIColors green, red and clear. Without this timer the color drags onto the next fruit.
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(updateUI), userInfo:nil, repeats: false)
}
// This check answer method needs an input to work. The input is the answer the user-choose (String).
func checkAnswer(userAnswer: String) -> Bool {
// Checks if the user got the answer correct. T/F
if userAnswer == fruitness.fruitOptions[fruitness.fruitNumber].fruit {
fruitness.score += 1 //We increase the value of score when we get the answer right.
// fruitness.finalScorez = fruitness.score
return true
} else {
return false
}
}
//Checks to make sure the eveytime it hits 0 it will shuffle.
func nextFruit() {
if fruitness.fruitNumber == 0 {
fruitness.fruitOptions.shuffle()
}
// print(fruitness.fruitOptions[fruitness.fruitNumber]) //Only gets printed in the consol
if fruitness.fruitNumber + 1 < fruitness.fruitOptions.count {
fruitness.fruitNumber += 1
} else {
self.performSegue(withIdentifier: "goToResultsVC", sender: self) //To call Segue
}
}
//Connecting and controlling oF the Segue and from GameView COntroller -> Results view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToResultsVC" {
let destinationVC = segue.destination as! ResultsViewController
destinationVC.finalScore = scoreLabel.text //Printing the final score at the end.
}
}
#objc func updateUI() {
//Controlls the background. Clearing ability between T/F answers.
optionButton0.backgroundColor = UIColor.clear
optionButton1.backgroundColor = UIColor.clear
optionButton2.backgroundColor = UIColor.clear
optionButton3.backgroundColor = UIColor.clear
optionButton4.backgroundColor = UIColor.clear
optionButton5.backgroundColor = UIColor.clear
optionButton6.backgroundColor = UIColor.clear
optionButton7.backgroundColor = UIColor.clear
optionButton8.backgroundColor = UIColor.clear
//The fruit name available that the user needs to match.
fruitLabel.text = fruitness.getFruitText()
//Displaying the progress of the user till they reach the end.
progressBar.progress = fruitness.getProgress()
//Displaying the score at all times
scoreLabel.text = "SCORE: \(fruitness.score)"
}
}
You have a number of problems with your code.
First off, the prepare(for:sender:) method gets called on the source view controller that triggers the segue, not the destination view controller.
Second, your ResultsViewController has a viewWillAppear(_:) method nested inside its prepare(for:sender:) method. Don't do that. The viewWillAppear(_:) method needs to be a top-level method of your view controller or it won't get called.
Also, viewWillAppear(_:) methods should call super.viewWillAppear(animated).
Your ResultsViewController's viewWillAppear method should look like this:
func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
finalScoreLabel.text = finalScore
}
The prepare(for:sender:) method in your GameViewController should set finalScore in your ResultsViewController so that when its viewWillAppear is called, finalScore has a value.
Edit:
(It looks like you got that last part correct. Your GameViewController's prepare(for:sender) does appear to set finalScore. Note, however, that you are using scoreLabel to hold your finalScoreValue. You should not save state in view objects. You should have a var in GameViewController that holds your score. You could make GameViewController set your score var, and have a didSet() method on the score var that installs the updated score value into the scoreLabel.)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToResultsVC" {
let destinationVC = segue.destination as! ResultsViewController
destinationVC.finalScore = scoreLabel.text //Printing the final score at the end.
}
}
Maybe you wish to refactor your repetitive code to this one:
let optionButtonArray = [optionButton0,optionButton1 ...]
optionButtonArray.forEach { $0.backgroundColor = UIColor.clear }

Is it possible to toss data to another view controller?

I just Make timer that can use in life. just like image that I push in here, if I go back to main ViewController then I wanna the number that I input in set view controller are tossed to viewController so when I go back to main ViewController and press restart then that number gonna be in text of CountTimeLabel.. but I really don't know how to toss data that I input in another view controller to root viewController... pleas help me.. if I write code like ViewController().variableName = 30 in setViewController, that dose not make things well..(I already know about prepare function but that is not what I am finding..because this is happen when I go back to ViewController(RootViewController)) I will put my code in below..
is it possible to toss data to another view controller from other view controller?
import UIKit
class ViewController: UIViewController{
#IBOutlet var AllTileLabel: UILabel!
#IBOutlet var SumTimeLabel: UILabel!
#IBOutlet var CountTimeLabel: UILabel!
#IBOutlet var StartButton: UIButton!
#IBOutlet var StopButton: UIButton!
#IBOutlet var ResetButton: UIButton!
var timeTrigger = true
var realTime = Timer()
var second : Int = 3000
var sum : Int = 14400
var allTime : Int = 14400
var IntSecond : Int = 0
var ifReset = false
override func viewDidLoad() {
StartButton.layer.cornerRadius = 10
StopButton.layer.cornerRadius = 10
ResetButton.layer.cornerRadius = 10
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func StartButtonAction(_ sender: UIButton) {
if timeTrigger { checkTimeTrigger() }
print("Start")
}
#IBAction func StopButtonAction(_ sender: UIButton) {
endGame()
}
#IBAction func ResetButtonAction(_ sender: UIButton) {
print(second)
getTimeData()
//second = 3000
//CountTimeLabel.text = "0:50:00"
CountTimeLabel.text = printTime(temp: second)
ifReset = true
}
#IBAction func Reset(_ sender: UIButton) {
endGame()
timeTrigger = true
realTime = Timer()
second = 3000
sum = 14400
allTime = 14400
IntSecond = 0
ifReset = false
AllTileLabel.text = "8:00:00"
SumTimeLabel.text = "0:0:0"
CountTimeLabel.text = "0:50:00"
}
#objc func updateCounter(){
// if String(format: "%.2f",second) == "0.00"{
if second < 1 {
endGame()
CountTimeLabel.text = "종료"
} else {
second = second - 1
sum = sum + 1
allTime = allTime - 1
AllTileLabel.text = printTime(temp: allTime)
SumTimeLabel.text = printTime(temp: sum)
CountTimeLabel.text = printTime(temp: second)
print("update")
}
}
func checkTimeTrigger() {
realTime = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
timeTrigger = false
}
func endGame() {
realTime.invalidate()
timeTrigger = true
}
func printTime(temp : Int) -> String
{
let S = temp%60
let H = temp/3600
let M = temp/60 - H*60
let returnString = String(H) + ":" + String(M) + ":" + String(S)
return returnString
}
func getTimeData() {
second = 20
sum = SetViewController().real.sum
allTime = SetViewController().real.allTime
print(second)
}
}
import UIKit
class SetViewController: UIViewController {
#IBOutlet var View1: UIView!
#IBOutlet var View2: UIView!
#IBOutlet var InputView1: UIView!
#IBOutlet var InputView2: UIView!
#IBOutlet var SetButton: UIButton!
#IBOutlet var H1TextField: UITextField!
#IBOutlet var M1TextField: UITextField!
#IBOutlet var H2TextField: UITextField!
#IBOutlet var M2TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
H1TextField.keyboardType = .numberPad
M1TextField.keyboardType = .numberPad
H2TextField.keyboardType = .numberPad
M2TextField.keyboardType = .numberPad
View1.layer.cornerRadius = 14
View2.layer.cornerRadius = 14
InputView1.layer.cornerRadius = 10
InputView2.layer.cornerRadius = 10
SetButton.layer.cornerRadius = 10
// Do any additional setup after loading the view.
}
#IBAction func SetButton(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
enter image description here
If you're a hobbyist programmer and you just want to "get it done", simply use a static.
Let's say Bottom: UIViewController is the "main", root, view controller at the absolute "base" of your app. no matter what happens "Bottom" is always there.
Say Timer: UIViewController is (any) other view controller you put on top for some reason.
In Bottom, do this
class Bottom: UIViewController, etc ... {
static weak var current: Bottom? = nil
override func viewDidLoad() {
super.viewDidLoad()
Bottom.current = self
}
func testing() {
print("it works, WTH")
}
Note that in ViewDidLoad, you simply set it.
Next, say you are in Timer, try this:
class Timer: UIViewController, etc ... {
func someFunction() {
Bottom.current.testing() // it's that easy
}
It's that easy.
Note there is a huge amount of confusion about using statics, singletons, and similar approaches in iPhone programming.
(Just for example, many engineers will say "avoid singletons!" This is remarkably confused because in iOS engineering, almost everything is a singleton (notably the app itself (!!!!!), the screen, the GPS, etc etc.)
In any event, as a beginner hobbyist, learn how to use statics (it's simple .. Bottom.current. ... as above), and eventually you can learn about the pros and cons of such things.

How to remove popView if we tap anywhere in the background except tapping in popView in swift

I have created one popView with textfield and button in ViewController. if i click button then popView is appearing, and i am able to enter text in textfield and submit is working, and if i tap anywhere in view also i am able to remove popView, but here i want if i tap on anywhere in popView i don't want to dismiss popView, Please help me in the code.
here is my code:
import UIKit
class PopUPViewController: UIViewController {
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
popView.isHidden = true
// Do any additional setup after loading the view.
}
#IBAction func butnAct(_ sender: Any) {
view?.backgroundColor = UIColor(white: 1, alpha: 0.9)
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView))
view.addGestureRecognizer(tap)
}
#objc func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}
}
In my code if i tap anywhere in the view popView is removing even if i tap on popView also its removing, i don't need that, if i tap on popView then popView need not to be remove.
Please help me in the code
You can override the touchesBegan method which is triggered when a new touch is detected in a view or window. By using this method you can check a specific view is touched or not.
Try like this
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if touch?.view != self.popView {
dismissView()
}
}
func dismissView() {
self.popView.isHidden = true
view?.backgroundColor = .white
}
It's not the way I would have architected this, but to get around the problem you face you need to adapt your dismissView method so that it only dismisses the view if the tap is outside the popView.
To do this modify your selector to include the sender (the UITapGestureRecogniser )as a parameter:
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(PopUPViewController.dismissView(_:)))
and then in your function accept that parameter and test whether the tap is inside your view, and if so don't dismiss the view:
#objc func dismissView(_ sender: UITapGestureRegognizer) {
let tapPoint = sender.location(in: self.popView)
if self.popView.point(inside: tapPoint, with: nil)) == false {
self.popView.isHidden = true
view?.backgroundColor = .white
}
}
Your Popup view is inside the parent view of viewcontroller that's why on tap of popview also your popview is getting hidden.
So to avoid just add a view in background and name it bgView or anything what you want and replace it with view. And it will work fine .
Code:
#IBOutlet weak var bgView: UIView!//Add this new outlet
#IBOutlet weak var popView: UIView!
#IBOutlet weak var inputField: UITextField!
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var submitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
popView.isHidden = true
}
#IBAction func butnAct(_ sender: Any) {
bgView.backgroundColor = UIColor(white: 1, alpha: 0.9)//change view to bgView[![enter image description here][1]][1]
popView.isHidden = false
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissView))
bgView.addGestureRecognizer(tap)//change view to bgView
}
#objc func dismissView() {
self.popView.isHidden = true
bgView.backgroundColor = .white//change view to bgView
}
#IBAction func sendButton(_ sender: Any) {
self.textLabel.text = inputField.text
}

Swift: Calling Functions from different View Controllers

Im very new to swift, I have a tabbed page app with 5 different view controllers and 5 different view controller.swift files.Each view controller has similar Syntax but a few names and labels are different. Each controller has a function that will restart my data values and they all work per page. but what I am trying to do, is access the clear functions from each view controller by just pressing one button from one view controller. My formatting/logic may be completely off. Im teaching myself so any help is appreciated
import UIKit
class FirstViewController: UIViewController {
// North wing
var nTotal = 0
var total = 0
//Stepper Values
#IBOutlet weak var tStepper: UIStepper!
#IBOutlet weak var cStepper: UIStepper!
#IBOutlet weak var ssStepper: UIStepper!
#IBOutlet weak var sStepper: UIStepper!
//-Tables
#IBOutlet weak var Tables: UILabel!
#IBAction func TCounter(_ sender: UIStepper) {
Tables.text = String(Int(sender.value))
}
//-Carrels
#IBOutlet weak var Carrels: UILabel!
#IBAction func CCounter(_ sender: UIStepper) {
Carrels.text = String(Int(sender.value))
}
//-Soft Seating
#IBOutlet weak var SoftSeating: UILabel!
#IBAction func SSCounter(_ sender: UIStepper) {
SoftSeating.text = String(Int(sender.value))
}
//-Standing
#IBOutlet weak var Standing: UILabel!
#IBAction func SCounter(_ sender: UIStepper) {
Standing.text = String(Int(sender.value))
}
//-Clear Pop-up
#IBAction func Clear(_ sender: UIButton) {
let alert = UIAlertController(title: "Clear", message: "Clear all or current page?", preferredStyle: .alert)
let clearPage = UIAlertAction(title: "Clear Page", style: .default, handler: {ACTION in
self.nClear()
})
let clearAll = UIAlertAction(title: "Clear All", style: .destructive, handler: {ACTION in
self.MasterClear()
})
let Cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(clearPage)
alert.addAction(clearAll)
alert.addAction(Cancel)
self.present(alert, animated: true, completion: nil)
}
This is the clear function on all of my view controllers
//Clear Function
func nClear() {
self.Tables.text = "0"
self.Carrels.text = "0"
self.SoftSeating.text = "0"
self.Standing.text = "0"
//Stepper Clear
self.tStepper.value = 0
self.cStepper.value = 0
self.ssStepper.value = 0
self.sStepper.value = 0
}
This is the function that I want to be able to access all the clear functions I have on every Controller
//Clear all Function
func MasterClear(){
self.nClear()
SecondViewController().iLClear()
}
my code breaks and app crashes when I run and access the master clear function
What you really want to do is make a model of the data you want to share between the different views one for each of your views. I will do a sample with one. create a swift file for your data model like this:
class TestModel {
var Tables: String = "0"
var Carrels: String = "0"
var SoftSeating: String = "0"
var Standing: String = "0"
//Stepper Clear
var tStepper: Int = 0
var cStepper: Int = 0
var ssStepper: Int = 0
func modelReset() {
Tables = "0"
Carrels = "0"
SoftSeating = "0"
Standing = "0"
tStepper = 0
cStepper = 0
ssStepper = 0
}
}
next in your first view controller add a variable that is an instance of the model
class FirstViewController: UIViewController {
var modelController: TestModel!
and in your viewDidLoad function initialize the variable
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
modelController = TestModel()
}
create your clear function. I called mine reset:
#IBAction func reset(_ sender: Any) {
//Do for each ViewConrtoller
modelController.modelReset()
}
now override the prepare function to send the data to the view controller we are going to
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondViewController = segue.destination as? SecondViewController {
secondViewController.modelController = modelController
}
}
now in your second view controller create a variable of the model but in this instance we will not initialize it in the viewDidLoad function.
class SecondViewController: UIViewController {
var modelController: TestModel!
we don't initialize it as we are passing it from the first view controller in our prepare function.
in the second view controllers viewWillApear function update the view with the data from the model
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.Tables.text = modelController.Tables
self.Carrels.text = modelController.Carrels
self.SoftSeating.text = modelController.SoftSeating
self.Standing.text = modelController.Standing
//Stepper Clear
self.tStepper.value = modelController.tStepper
self.cStepper.value = modelController.cStepper
self.ssStepper.value = modelConroller.ssStepper
}
one last step don't forget to implement the prepare function to pass the model back to the first view controller in case you have updated it
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondViewController = segue.destination as? SecondViewController {
secondViewController.modelController = modelController
}
}

Swift - Delegate through Nav Controller

I'm going to include my full code in this but I will try to give pointers to where the relevant bits are. Basically I am returning to a view controller from an Unwind Segue with some new data. I am using that data successfully in the 'NBARotoHome' VC but I additionally need to pass some of that data through an embedded Nav controller to 'NBARotoTabPager' vc.
I am trying to do this using the 'UpdateChildView' delegate (at the top of the first block of code) and calling its method in 'loadViewData() (in the 'if statement' near the bottom of the first block).
protocol UpdateChildView : class {
func updateView()
func test()
var playerSelected: Player? { get set }
}
class RotoViewRoundCell: UITableViewCell{
#IBOutlet weak var categoryLabel: UILabel!
}
class RotoViewRoundHeader: UITableViewCell{
}
class NBARotoHome: UIViewController{
#IBOutlet weak var posPaidLabel: UILabel!
#IBOutlet weak var progressIndicator: UIProgressView!
#IBOutlet weak var vsLabel: UILabel!
#IBOutlet weak var fantasyFundsAmountLabel: UILabel!
#IBOutlet weak var fantasyFundsLabel: UILabel!
#IBOutlet weak var playerName: UILabel!
#IBOutlet weak var roundIndicator: UILabel!
var selectedPlayer: Player!
var firstNavController: UINavigationController!
weak var updateChildView : UpdateChildView?
override func viewDidLoad() {
super.viewDidLoad()
loadViewData()
firstNavController = self.navigationController
let rightBarButton = UIBarButtonItem(title: "Select", style: UIBarButtonItemStyle.plain, target: self, action: #selector(myRightSideBarButtonItemTapped(_:)))
self.navigationItem.rightBarButtonItem = rightBarButton
self.title = "NBA Roto"
}
func myRightSideBarButtonItemTapped(_ sender:UIBarButtonItem!){
performSegue(withIdentifier: "ShowDraft", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowDraft" {
let navVC = segue.destination as? UINavigationController
let nbaDraftList = navVC?.viewControllers.first as! NBADraftList
nbaDraftList.mainNavController = firstNavController
}
if (segue.identifier == "buyNavControllerChild"){
// let navVC = segue.destination as? UINavigationController
// let buyVC = navVC?.viewControllers.first as! NBARotoTabPager
// buyVC.delegate = self
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
}
func loadViewData(){
if((selectedPlayer) != nil){
roundIndicator.text = "Upcoming: " + selectedPlayer.game_time
playerName.text = selectedPlayer.Name
vsLabel.text = selectedPlayer.visiting + " # " + selectedPlayer.home
fantasyFundsLabel.text = ""
fantasyFundsAmountLabel.text = ""
updateChildView?.test()
// updateChildView?.playerSelected = selectedPlayer
// updateChildView?.updateView()
}else{
roundIndicator.text = "Select a Player"
playerName.text = "No Player Selected"
vsLabel.text = "--"
fantasyFundsLabel.text = "Fantasy Funds"
fantasyFundsAmountLabel.text = "$10,000"
}
}
}
Because I haven't been able to get the delegate to work, I have been playing around with setting its delegate property in the above 'prepare' method -'buyVC.delegate = self' - but I'm getting 'buyVC has no member delegate' so that has been a dead end.
The next bit of code is the NBARotoTabPager vc which is embedded in the navigation controller. For reasons I'm no longer sure about I decided to make it a subclass of NBARotoHome, but its basically a custom tab pager that uses a segmented control to switch between two additional vcs.
The most important step at this point is just getting the 'test' function to work (which just prints 'test'. Its implemented in the below block of code second from the bottom above updateView).
class NBARotoTabPager: NBARotoHome, UpdateChildView{
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var scoreKey: UIBarButtonItem!
#IBOutlet weak var standings: UIBarButtonItem!
var playerSelected: Player?
override func viewDidLoad() {
navigationController?.navigationBar.barTintColor = UIColor(red: 27/255, green: 27/255, blue: 27/255, alpha: 1)
scoreKey.setTitleTextAttributes([NSFontAttributeName: UIFont(name: "Helvetica", size: 13.0)!], for: UIControlState.normal)
scoreKey.tintColor = UIColor.blue
standings.setTitleTextAttributes([NSFontAttributeName: UIFont(name: "Helvetica", size: 13.0)!], for: UIControlState.normal)
standings.tintColor = UIColor.blue
setupView()
}
private func setupView() {
setupSegmentedControl()
updateView()
}
private func setupSegmentedControl() {
// Configure Segmented Control
segmentedControl.removeAllSegments()
segmentedControl.insertSegment(withTitle: "Live", at: 0, animated: false)
segmentedControl.insertSegment(withTitle: "Avg / +", at: 1, animated: false)
segmentedControl.addTarget(self, action: #selector(selectionDidChange(_:)), for: .valueChanged)
segmentedControl.selectedSegmentIndex = 0
}
func selectionDidChange(_ sender: UISegmentedControl) {
updateView()
}
private lazy var viewLiveTab: NBARotoLive = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "NBARotoLive") as! NBARotoLive
if((self.playerSelected) != nil){
viewController.selectedPlayer = self.playerSelected
}
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
private lazy var viewAvgsTab: NBARotoAvgs = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "NBARotoAvgs") as! NBARotoAvgs
if((self.playerSelected) != nil){
viewController.selectedPlayer = self.playerSelected
}
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
private func add(asChildViewController viewController: UIViewController) {
// Add Child View Controller
addChildViewController(viewController)
// Add Child View as Subview
view.addSubview(viewController.view)
// Configure Child View
viewController.view.frame = view.bounds
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Notify Child View Controller
viewController.didMove(toParentViewController: self)
}
private func remove(asChildViewController viewController: UIViewController) {
// Notify Child View Controller
viewController.willMove(toParentViewController: nil)
// Remove Child View From Superview
viewController.view.removeFromSuperview()
// Notify Child View Controller
viewController.removeFromParentViewController()
}
internal func test(){
print("test")
}
internal func updateView() {
if segmentedControl.selectedSegmentIndex == 0 {
let position = viewAvgsTab.tableView.contentOffset.y;
viewLiveTab.tableView.contentOffset = CGPoint(x:0, y:position);
remove(asChildViewController: viewAvgsTab)
add(asChildViewController: viewLiveTab)
} else {
let position = viewLiveTab.tableView.contentOffset.y;
viewAvgsTab.tableView.contentOffset = CGPoint(x:0, y:position);
remove(asChildViewController: viewLiveTab)
add(asChildViewController: viewAvgsTab)
}
}
}
I've looked at a lot of examples but I don't understand the whole 'setting the delegate' thing i.e. theSecondViewController.delegate = self. Sometimes I see examples where you don't need to do this. And other times it seems like my VCs don't even have a delegate property. So I'm not sure if that's my specific problem or not but any direction would be greatly appreciated. Thanks
There are three steps to implement a delegate.
create a protocol.. (you've already done this by creating a updateChildView protocol)
you need to implement this protocol in the class you wish to receive and process this data.. (you've not done this step and thats why you cant set buyVC.delegate = self)
you need to add a property in ViewController2 called "delegate" and make it as a type of your protocol in step 1 (you've not done this step and there is no property called "delegate" in vc2 .. that's why you get this error 'buyVC has no member delegate')
Here's a quick example:
Protocol:
protocol UpdateChildView{ //removed :class
func updateView()
func test()
var playerSelected: Player? { get set }
}
Viewcontroller A:
class NBARotoHome: UIViewController, UpdateChildView { //added conformance to the protocol
//add the methods for conforming to protocol and add your implementation
func updateView() {
//add your implementation
}
func test(){
//add your implementation
}
var playerSelected: Player? {
//add your implementation
}
prepare(for: Segue) {
/** code for passing data **/
let navVC = segue.destination as? UINavigationController
let buyVC = navVC?.viewControllers.first as! NBARotoTabPager
buyVC.delegate = self
//sets the delegate in the new viewcontroller
//remember.. delegate is a property in the next vc
// and the property only accepts any viewcontroller that is implements updatechildview protocol
present(vc2)
}
}
viewcontroller2 :
class viewControllerB: UIViewController {
var delegate: UpdateChildView? //add this
viewdidload {
delegate?.test() //call the func in the previous vc
}
}

Resources