How to transfer array variables between tab bar controllers? - swift

I'm trying to understand the life cycle of view controllers. I need to use TabBar to switch controllers. And when switching controllers, I need the label to display life-cycle methods, not only of the controller on which I now find, but also from others.
I created an empty array private var arrayOfMethods = [String]() in which I add a triggered method every time.
class ViewController: UIViewController {
private var arrayOfMethods = [String]()
#IBOutlet var greenLabel: UILabel!
#IBOutlet var blueLabel: UILabel!
#IBOutlet var yellowLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
printMessage()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
printMessage()
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
printMessage()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
printMessage()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
printMessage()
}
func printMessage(line: Int = #line, function: String = #function) {
print("\(title ?? "nil"). Line: \(line) of function \(function)")
arrayOfMethods.append(function)
let string = arrayOfMethods.joined(separator: "\n")
greenLabel.text = "\(title ?? "nil") \(string)"
}
}

You can create a common class and call its function from all of your view controllers
class Helper: NSObject {
private var arrayOfMethods = [String]()
static let shared = Helper()
let mainLabel = UITextView(frame: CGRect(x: 100, y: 100, width: 300, height: 300))
private override init() {
super.init()
}
func printMessage(vc: UIViewController, line: Int = #line, function: String = #function) {
print("\(vc.title ?? "nil"). Line: \(line) of function \(function)")
arrayOfMethods.append( (vc.title ?? "nil") + "-" + function)
let string = arrayOfMethods.joined(separator: "\n")
guard let window = UIApplication.shared.keyWindow else { return }
if !window.subviews.contains(mainLabel) {
window.addSubview(mainLabel)
window.bringSubviewToFront(mainLabel)
}
mainLabel.text = string
}
}
And call this singleton class method from all your view controllers like this
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Helper.shared.printMessage(vc: self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Helper.shared.printMessage(vc: self)
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Helper.shared.printMessage(vc: self)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
Helper.shared.printMessage(vc: self)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
Helper.shared.printMessage(vc: self)
}
}
Output

Related

viewWillDisapper isn't called when opening a second view with popOver

On my mainVC I have a TableView with a button that should open the secondVC, where I can add things to show then in the Table, with the kind: "Present As Popover".
I'm opening the secondVC with performSegue(withIdentifier:"goToOtherView", sender: nil.
class FirstViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var groupData = ["Data, Data1, Data2"]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
tableView.delegate = self
tableView.dataSource = self
}
#IBAction func btnTapped(_ sender: Any) {
performSegue(withIdentifier: "goToOtherView", sender: nil)
}
override func viewWillAppear(_ animated: Bool) {
print("FirstViewController will appear")
}
override func viewDidAppear(_ animated: Bool) {
print("FirstViewController did appear")
}
override func viewWillDisappear(_ animated: Bool) {
print("FirstViewController will disappear")
}
override func viewDidDisappear(_ animated: Bool) {
print("FirstViewController did disappear")
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
}
override func viewWillAppear(_ animated: Bool) {
print("SecoundViewController will appear")
}
override func viewDidAppear(_ animated: Bool) {
print("SecoundViewController did appear")
}
override func viewWillDisappear(_ animated: Bool) {
print("SecoundViewController will disappear")
}
override func viewDidDisappear(_ animated: Bool) {
print("SecoundViewController did disappear")
}
}
Storyboard
When I start the App
When I open the secondVC
And when I close the secondVC
The FirstViewController willDisappear and didDisappear are never being called, or even when the secondVC is closed the willAppear and didAppear. I want to call the function tableView.reloadData() when the FirstView willAppear/didAppear
It's kinda the same like the IOS default clock app, when you add a new alarm clock.
Quick guess, but I think that’s because of the presentation mode being pageSheet
Can you try to change it to fullScreen?
You can also change your segue configuration to be like:
View disappear won't be called since the view does not completely disappears from the window

How to send an array of struct between two view controllers?

Im trying to send an array "ranking" of Struct between two VC after tapping the "rankingButton". I don't know what to code in the second vc to get and manipulate the array.
First VC:
struct TopPoint{
let user: String
let points: Int
var position: Int
}
var ranking: [TopPoint] = []
override func viewDidLoad() {
super.viewDidLoad()
let user1 = TopPoint(user: "fran", points: 1324, position: 1)
ranking.append(user1)
}
#IBAction func rankingButton(_ sender: Any) {
let vc = TopUserTableViewController(nibName: "TopUserTableViewController", bundle: nil)
vc.ranking = ranking
navigationController?.pushViewController(vc, animated: true)
}
Second VC:
class TopUserTableViewController: UITableViewController {
var ranking = [TopPoint]()
override func viewDidLoad() {
super.viewDidLoad()
}
//what to code to get the array?
}
Once your secondVC pushed, you can directly access the array in the secondVC. Because you set its ranking property equal to ranking in the firstVC For example consider having a method to iterate through array elements and print user of each struct element inside your array. You can do it by:
class TopUserTableViewController: UITableViewController {
var ranking: [TopPoint]!
override func viewDidLoad() {
super.viewDidLoad()
iterateElements()
}
func iterateElements() {
ranking.forEach { (element) in
print(element.user)
}
}
}
You could also access directly inside your viewDidLoad, I added iterateElements method as reference, access directly in viewDidLoad as:
override func viewDidLoad() {
super.viewDidLoad()
ranking.forEach { (element) in
print(element.user)
}
}
viewDidLoad() is too early in the lifecycle. Just move your code:
func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
ranking.forEach { (element) in print(element.user) } }
}

swift how to update first controller from logic in second one vai protocol and delegate pattern?

I have a label in first view controller ViewController, and a func getting date avery second in second vc. I'd like to update label in first after timer starts in second. is it good to use protocol-delegate pattern? at this moment it is not working, time is going but not updating the view in first VC
my struct for protocol
protocol ViewControllerDelegate: class {
func changeLabelText(textToPass: String)
}
in first viewController
class ViewController: UIViewController, ViewControllerDelegate {
#IBOutlet weak var mainLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func changeLabelText(textToPass: String) {
self.mainLabel.text = textToPass
self.view.layoutIfNeeded()
}
#IBAction func buttonTapped(_ sender: UIButton) {
let nextVC = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
nextVC.delegateSubscriber = self
present(nextVC, animated: true, completion: nil)
}
}
in secondVC
class SecondViewController: UIViewController {
//MARK: speed timer feature 1/3
private weak var timer: Timer?
private var timerDispatchSourceTimer : DispatchSourceTimer?
weak var delegateSubscriber : ViewControllerDelegate?
#IBOutlet weak var myTxtField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
startTimer(every: 1)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("appeared")
stopTimer()
}
private func startTimer(every timeInterval: TimeInterval) {
if #available(iOS 10.0, *) {
timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: true) { [weak self] _ in
let dateToPass = Date().description
print(dateToPass)
self?.delegateSubscriber?.changeLabelText(textToPass: dateToPass)
}
}
}
//MARK: speed timer feature 3/3
private func stopTimer() {
timer?.invalidate()
//timerDispatchSourceTimer?.suspend() // if you want to suspend timer
timerDispatchSourceTimer?.cancel()
}
#IBAction func buttonTapped(_ sender: UIButton) {
// delegateSubscriber?.changeLabelText(textToPass: self.myTxtField.text ?? "error")
dismiss(animated: true, completion: nil)
}
}
Just remove [weak self] from Timer closure
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
let dateToPass = Date().description
print(dateToPass)
self.delegateSubscriber?.changeLabelText(textToPass: dateToPass)
}
... then self isn't optional

update UIViewController in Real Time from Popover Viewcontroller in Swift 4

right now i'm experimenting with SceneKit DebugOptions.
i'm trying to update/ show Scenekits Debug Options in real time, using switch controllers from a Popover ViewController.
i've tried many things, like UserDefaults, Delegation and Protocols, but stil i wasn't able to see the result in real time, every time i have to kill the app en relaunch it to see the results.
so, i would be greatfull if someone would have an answer to my question :D
extension i added to my MainVC
extension ViewController: UIPopoverPresentationControllerDelegate, DebugOptions {
func wireFrameEnabled(enabled: Bool) {
if enabled == true {
print(enabled)
}
}
func showCameraEnabled(enabled: Bool) {
}
func showAllDebugOptions(enabled: Bool) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let popoverController = segue.destination.popoverPresentationController, let button = sender as? UIButton else { return }
popoverController.delegate = self
popoverController.sourceRect = button.bounds
let debugMenuVC = popoverController.presentedViewController as! DebugMenuVC
debugMenuVC.delegate? = self
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
}
Protocol
protocol DebugOptions {
func wireFrameEnabled(enabled: Bool)
func showCameraEnabled(enabled: Bool)
func showAllDebugOptions(enabled: Bool)
}
DebugMenuVC
class DebugMenuVC: UIViewController {
#IBOutlet weak var bgView: UIView!
#IBOutlet weak var showWireFrameSwitch: UISwitch!
#IBOutlet weak var showCameraSwitch: UISwitch!
#IBOutlet weak var showAllSwitch: UISwitch!
var delegate: DebugOptions?
override func viewWillLayoutSubviews() {
preferredContentSize = CGSize(width: 150, height: 300)
}
override func viewDidLoad() {
super.viewDidLoad()
buttonCheck()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
#IBAction func aSwitchBtnWasPressed( _ sender: UISwitch ) {
if (sender.tag == 0) && (sender.isOn == true) {
userDefaults.set(true, forKey: SHOW_WIRE_FRAME)
delegate?.wireFrameEnabled(enabled: true)
} else if (sender.tag == 0) && (sender.isOn == false) {
userDefaults.set(false, forKey: SHOW_WIRE_FRAME)
delegate?.wireFrameEnabled(enabled: false)
}
}
func buttonCheck() {
if userDefaults.bool(forKey: SHOW_WIRE_FRAME) == true{
showWireFrameSwitch.isOn = true
} else {
showWireFrameSwitch.isOn = false
}
}
}
in debubMenuVC.delegate shouldn't be an optional. thats the reason the delegation method always failed :D
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let popoverController = segue.destination.popoverPresentationController, let button = sender as? UIButton else { return }
popoverController.delegate = self
popoverController.sourceRect = button.bounds
let debugMenuVC = popoverController.presentedViewController as! DebugMenuVC
debugMenuVC.delegate? = self
}

how to get a textview to autoscroll down when theres a new line

in swift 4.0 for cocoa applications
how do you get a textview to autoscroll down when theres a new line
of text added into it?
is there a built in function for this?
i can't seem to find a way to do this.
chatplace.scroll(<#T##point: NSPoint##NSPoint#>)
Here's a simple example that works:
import UIKit
class ViewController: UIViewController {
var count = 0
var timer:Timer?
#IBOutlet weak var textView: UITextView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
textView.isScrollEnabled = true
textView.becomeFirstResponder()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
timer = Timer(timeInterval: 0.125, repeats: true, block: { (timer) in
self.count += 1
self.textView.text.append("\n\(self.count) This is another line")
})
if let timer = timer {
RunLoop.current.add(timer, forMode: .commonModes)
}
}
}
The result:
If you want to scroll programmatically this should do it:
func scrollToEnd(_ someTextView:UITextView) {
let bottom = NSMakeRange(someTextView.text.lengthOfBytes(using: .utf8)-1, 1)
someTextView.scrollRangeToVisible(bottom)
}