How to trigger a method on my container view manually? - swift

I do have a View, in which I embedded a ContainerView. I fill my labels on my ContainerView the first time here
class UpperLower { ...
override func prepare(for segue: UIStoryboardSegue, sender: Any!) {
let PlayerInfoHeaderView = segue.destination as? PlayerInfoHeader
PlayerInfoHeaderView?.player1 = player1
PlayerInfoHeaderView?.player2 = player2
PlayerInfoHeaderView?.game = game
}
}
The Segue triggers the viewDidLoad() where I call the method updateUI()
class PlayerInfoHeader: UIViewController { ...
override func viewDidLoad() {
super.viewDidLoad()
updateUI(game: game)
}
func updateUI(game: Player.Game) {
player1NameLabel.text = player1.name
player2NameLabel.text = player2.name
switch game {
case .UpperLower:
player1PointsLabel.text = "Points: \(player1.points.UpperLower)"
player1WinrateLabel.text = "Winrate: \(player1.winrates.UpperLower) %"
player1RoundsWonLabel.text = "Rounds Won: \(player1.roundswon.UpperLower)"
player2PointsLabel.text = "Points: \(player2.points.UpperLower)"
player2WinrateLabel.text = "Winrate: \(player2.winrates.UpperLower)"
player2RoundsWonLabel.text = "Rounds Won: \(player2.roundswon.UpperLower)"
}
Now, after every round played, I also want to update my UI. I tried a lot of things, but I have no clue, how to trigger the UpdateUI() out of my UpperLower manually. I know that I need a reference to my embedded container view. But how can I get this reference outside of the segue context? Is there an easy way to solve my problem?
PS: I did all my UI work on the storyboard.

Set a weak property (to avoid retain cycle) in PlayerInfoHeader to keep a reference to your parent
weak var parentVC: UpperLower?
Set the property in prepareForSegue
PlayerInfoHeaderView.parentVC = self

Related

Why Does My Object Not Transfer To The Next VC?

I am trying to transfer my object from HockeyDetailVC to my FavouritesVC using a button but my object is nil when I reach my second VC FavouritesVC. Why is it like that when I set the variable in my firstVC with my func transferObj()?
HockeyDetailVC
var item: CurrentPlayers?
override func viewDidLoad() {
super.viewDidLoad()
gonnaLoadView()
tableV.bounces = false
tableV.alwaysBounceVertical = false
favButton.layer.cornerRadius = 10
print(item) *//prints my current players object*
}
func transferObj() {
let otherVC = FavouritesVC()
otherVC.currentFav = item
print(item). *//prints my current player object*
}
#IBAction func addToFav(_ sender: Any) {
transferObj()
print("Favourite button Pressed")
}
FavouritesVC
var currentFav: CurrentPlayers?
override func viewDidLoad() {
super.viewDidLoad()
if currentFav == nil {
//display nil
self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
print(favArr) *//prints empty array*
print(currentFav) *//nil*
} else {
favArr.append(currentFav!)
print(favArr)
}
}
As #Martin stated, let otherVC = FavouritesVC() creates a new instance of the controller, but it is not the instance that you will eventually display. So you are effectively setting the currentFav of a random FavouritesVC that will never actually be displayed, while the one you eventually do navigate to has it's currentFav property still unset.
To set the appropriate FavouritesVC instance, you need to access it in one of several ways (depending on how you present it). If it is through a segue, then you can reference it in the prepare(for segue: sender:) method. (When you create a Cocoa Touch Class file, the below method template is pre-populated. As it states, reference the new view controller using segue.destination.)
/*
// 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.
}
*/
Alternatively, if you create and present the new view controller programmatically with something like
// 1.
let otherVC = storyboard?.instantiateViewController(withIdentifier: "yourFavouritesVCIdentifier")
// 2.
// 3.
self.show(otherVC, sender: self)
you can insert your otherVC.currentFav = item at line // 2..

Rx BehaviorRelay doesnt keep the data when return from a view

I have an app that have 3 views and a service logic layer. The services is sending messagings all the time to all this views.What I do to control all this data was use RxBehaviorRelay.
To this point all works great, I match the variables with the services layer and I am receiving data in the ViewController 1, 2 and 3.
The problem comes when I am on the view2 and then go to view3 and return to view2. On this return I lost all the changes and the BehaviorRelay object is like the beginning.
My code in the view1 is like this one
class View1: UIViewController {
var service: ServiceModel? = ServiceModel()
var observableView1 = BehaviorRelay(value: [Object]())
var observableView2 = BehaviorRelay(value: [Object]())
override func viewDidLoad() {
service!.observableView1 = self.observableView1
service!.observableView2 = self.observableView2
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destination = segue.destination as! View2
destination.observableView2 = self.observableView2
}
}
Code of view2
class View2: UIViewController {
let disposeBag = DisposeBag()
var observableView2 = BehaviorRelay(value: [Object]())
override func viewDidLoad() {
fillTable()
}
func fillTable() {
observableView2.asObservable().bind(to: tvData.rx.items) { (tableview, row, data) in
//code to fill the table
}.disposed(by: disposeBag)
}
}
On the service layer I only receive the changes and put in the BehaviorRelay object.
observableView2.accept(changes)
observableView1.accept(changes)
As I said I obtain all the changes in the view2 but when I move to view3 and return to view2, the changes gone.
Sorry for my english.
Thanks
All relays, subjects and observables should always be declared using let, never with var. Once you make this fix, you will notice why you are loosing your changes.
Based on your description, I would expect something more like:
final class Service {
private let _observableView1 = BehaviorRelay(value: [Object]())
private let _observableView2 = BehaviorRelay(value: [Object]())
let observableView1: Observable<[Object]>
let obserrvableView2: Observable<[Object]>
init() {
observableView1 = _observableView1.asObservable()
observableView2 = _observableView2.asObservable()
}
}
Then pass the Service to View 2, or subscribe to the service inside the prepare method.

How to update information on previous page of navigation controller without relaunching page?

I am making a quiz app and have three view controllers and I am also making use of a navigation controller. The controllers are: HomeController, LevelsController, PlayController.
The levels controller has 100 levels on it as buttons and all of them except level 1 are greyed out initially. As I complete levels, the other level buttons are unlocked and become clickable.
MY ISSUE:
This only works if I go back to the HomeController and press on the then navigate to the LevelsController again, it doesn't update instantly when I simply go back from the PlayController to the LevelsController.
This is my code in the levels controller:
import UIKit
class LevelsViewController: UIViewController {
var levels = [String]()
override func viewDidLoad() {
super.viewDidLoad()
for k in globalScore...99 {
// print("this is \(k)")
// print(levelButton)
levelButton[k].isEnabled = false
print("Have the levels updated??")
}
}
override func viewWillAppear(_ animated: Bool) {
for k in globalScore...99 {
levelButton[k].isEnabled = false
}
}
#IBOutlet var levelButton: [UIButton]!
#IBAction func levelSelect(_ sender: UIButton) {
self.level = sender.tag
performSegue(withIdentifier: "LevelToPlaySegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("my name is jacobson")
var vc2 = segue.destination as! PlayViewController
vc2.enteredLevel = self.level
}
}
My PlayController has a keyboard and an image that changes to a new image if the correct answer is input into a text field. I also have a score in the top right of the PlayController which is assigned to globalScore. as the global score increases, the levels page should have more levels available to the user when they go backwards from the PlayController to the LevelsController.
What you need to do is to change your logic and instead of using it on viewDidLoad, you should also add ViewDidAppear and ViewWillAppear, in order to call these method every time you open the viewController instead of doing it only the first time.
Jasc24 has answered this already but I'm writing exactly what worked for me.
I wrote this in my ViewDidLoad to make all levels except level 1 disabled:
for k in 1...99 {
levelButton[k].isEnabled = false
}
I wrote this after the ViewDidLoad to enable specific levels only:
override func viewWillAppear(_ animated: Bool) {
for k in 0...globalScore-1 {
levelButton[k].isEnabled = true
}
}

UITapGesture inside Framework not working

I'm trying to create my first Cocoapod framework, and need to attach a simple UITapGestureRecognizer to a view, but I can't get the tap gesture action to be called from within my framework. I've got:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let foo = Foo()
foo.attachTo(view: view)
}
}
I created a framework using pod lib create Foo, inside is
public class Foo {
public init() {}
public func attachTo(view: UIView) {
let endpointGesture = UITapGestureRecognizer(target: self, action: #selector(selected(_:)))
view.backgroundColor = UIColor.blue
view.isUserInteractionEnabled = true
view.addGestureRecognizer(endpointGesture)
}
#objc private func selected(_ sender: UITapGestureRecognizer) {
print("Gesture Recognized")
}
}
I can tell the view is correctly passed into the framework because building the app gives me a blue screen.
Moving the gesture recognizer and selected function into ViewController works as expected as well. Tapping on the view prints Gesture Recognized to the console, so there's something going on with the framework I don't understand.
I have already tried adding the -ObjC linker flag to the framework, but that doesn't seem to have done anything. Am I missing something?
The problem is that your foo variable is not retained.
If you make the foo variable as instance variable it should work.
class ViewController: UIViewController {
let foo = Foo() // this is now retained by the view controller
override func viewDidLoad() {
super.viewDidLoad()
foo.attachTo(view: view)
}
}

Swift; delegate embedded view controller and parent

Sorry in advance that I can’t explain myself very well. I’m really new to programming and the topic of delegation still eludes me. I had some great help with this once before, but now I am trying to use a delegate in a different situation and I can’t get it right. I pieced together a bit of code that doesn’t work, and no matter how much I search I can’t find a way to fix it.
I have a view controller (MainController) with and embedded view controller (EmbeddedController) in a container view. I am trying to have a button in the embedded controller manipulate the container view (containerView).
EmbeddedController:
protocol ControllerDelegate {
func hideContainerView()
}
class EmbeddedController: UIViewController {
var delegate: VControllerDelegate?
#IBAction func button(sender: AnyObject) {
delegate?.hideContainerView()
}
}
MainController:
class MainController: UIViewController, ControllerDelegate {
#IBOutlet var containerView: UIView!
func hideContainerView() {
containerView.hidden = true
}
override func viewDidLoad() {
super.viewDidLoad()
var vc = EmbeddedController()
vc.delegate = self
}
}
Does anyone have any idea what I am doing wrong? And why this isn’t working?
What I ended up doing is adding this to the MainController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "mySegue") {
let vc = segue.destinationViewController as! EmbeddedController
vc.delegate = self
}
}
In storyboard I selected the segue from the MainController to the EmbeddedController, and set the identifier to "mySegue".
Without the code above the delegate kept returning nil. I didn't look into this solution at first as I thought segues were only for transitioning between view controllers, and in my mind I didn't see the embedded controller as a transition. Maybe someone more knowledgable than me (which is practically anyone on here at this point) can explain how this is all fitting together.
In any case, this is how I solved my issue and hopefully someone else can benefit from this as well :)
First of all, to avoid strong reference cycles:
protocol ControllerDelegate: class {
func hideContainerView()
}
class EmbeddedController: UIViewController {
weak var delegate: ControllerDelegate?
And you haven't added your newly instantiated VC view to container view, and not added it as a child VC:
let vc = EmbeddedController()
vc.delegate = self
containerView.addSubview(vc.view)
self.addChildViewController(vc)
vc.didMoveToParentViewController(self)