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
}
Related
I put a container inside my MainViewController. Then added FirstViewController inside that container
let containerView = UIView()
view.addSubview(containerView)
let controller = FirstViewController()
addChildViewController(controller)
containerView.addSubview(controller.view)
controller.didMove(toParentViewController: self)
There are a UITextField and a UIButton inside FirstViewController. There is a label inside SecondViewController and var passedText = "" to update the UILabel text. I just want to set the UILabel text according to the UITextField text from FirstViewController when I press UIButton and it should leads to SecondViewController as well. This is the UIButton action method from FirstViewController. I'm not using Storyboard
#IBAction func btnPressed(_ sender: Any) {
let secondVC = SecondViewController()
addChildViewController(secondVC)
view.addSubview(secondVC.view)
secondVC.view.frame = view.bounds
secondVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
secondVC.didMove(toParentViewController: self)
secondVC.passedText = inputTextField.text!
}
This is FirstViewController
class FirstViewController: UIViewController {
#IBOutlet weak var inputTextField: UITextField!
let secondVC = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPressed(_ sender: Any) {
addChildViewController(secondVC)
view.addSubview(secondVC.view)
secondVC.view.frame = view.bounds
secondVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
secondVC.didMove(toParentViewController: self)
secondVC.passedText = inputTextField.text!
}
}
This is SecondViewController
class SecondViewController: UIViewController {
var passedText = ""
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = passedText
}
}
Closure
class ParentViewController: UIViewController {
var embeddedViewController: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
let firstViewController = FirstViewController()
embedViewController(firstViewController)
firstViewController.passDataClosure = { [weak self] text in
let secondViewController = SecondViewController()
self?.embedViewController(secondViewController)
secondViewController.passedText = text
}
}
func embedViewController(_ viewController: UIViewController) {
removeEmbedded()
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addChildViewController(viewController)
view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
embeddedViewController = viewController
}
func removeEmbedded() {
embeddedViewController?.view.removeFromSuperview()
embeddedViewController?.didMove(toParentViewController: nil)
embeddedViewController?.removeFromParentViewController()
}
}
-
class FirstViewController: UIViewController {
var passDataClosure: ((String?) -> Void)?
#IBOutlet weak var inputTextField: UITextField!
#IBAction func btnPressed(_ sender: Any) {
passDataClosure?(inputTextField.text)
}
}
-
class SecondViewController: UIViewController {
var passedText: String? {
didSet {
label.text = passedText
}
}
#IBOutlet weak var label: UILabel!
}
Delegate
class ParentViewController: UIViewController, FirstViewControllerDelegate {
var embeddedViewController: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
let firstViewController = FirstViewController()
embedViewController(firstViewController)
firstViewController.delegate = self
}
func embedViewController(_ viewController: UIViewController) {
removeEmbedded()
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addChildViewController(viewController)
view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
embeddedViewController = viewController
}
func removeEmbedded() {
embeddedViewController?.view.removeFromSuperview()
embeddedViewController?.didMove(toParentViewController: nil)
embeddedViewController?.removeFromParentViewController()
}
// MARK: FirstViewControllerDelegate
func firstViewController(_ firstViewController: FirstViewController, pass text: String?) {
let secondViewController = SecondViewController()
embedViewController(secondViewController)
secondViewController.passedText = text
}
}
-
class SecondViewController: UIViewController {
var passedText: String? {
didSet {
label.text = passedText
}
}
#IBOutlet weak var label: UILabel!
}
-
protocol FirstViewControllerDelegate: class {
func firstViewController(_ firstViewController: FirstViewController, pass text: String?)
}
class FirstViewController: UIViewController {
weak var delegate: FirstViewControllerDelegate?
#IBOutlet weak var inputTextField: UITextField!
#IBAction func btnPressed(_ sender: Any) {
delegate?.firstViewController(self, pass: inputTextField.text)
}
}
As far as I can see, this should work:
secondVC.label.text = inputTextField.text
You currently have secondVC.label = inputTextField.text which shouldn't even compile.
you can create a singleton class for your scenario. so that you can access all of your important data from anywhere either from your firstViewController or SecondViewController.
class YourDataClass : NSObject{
var textFieldData = String()
var lblSeconddata = String()
struct Static
{
public static var instance: YourDataClass?
}
class var singleton: YourDataClass
{
if Static.instance == nil
{
Static.instance = YourDataClass()
}
return Static.instance!
}
}
now somewhere in your controller
YourDataClass.Singleton.textFieldData = txtFld.Text
YourDataClass.Singleton. lblSeconddata = lblText.Text
in smame way you can get it anywhere
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
}
}
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
I have completed all the needed code for delegate to work. In my viewcontroller:
class ViewController: UIViewControllerCustomViewDelegate
I also have this:
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
func goToNextScene() {
print("GOTOSCENE2")
}
And in my custom view I have this:
import UIKit
protocol CustomViewDelegate: class { // make this class protocol so you can create `weak` reference
func goToNextScene()
}
#available(iOS 10.0, *)
class ImageHeaderView : UIView {
#IBOutlet weak var followme: UISwitch!
#IBOutlet weak var profileImage : UIImageView!
#IBOutlet weak var backgroundImage : UIImageView!
weak var delegate: CustomViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor(hex: "E0E0E0")
self.profileImage.layer.cornerRadius = self.profileImage.frame.size.height / 2
self.profileImage.clipsToBounds = true
self.profileImage.layer.borderWidth = 1
self.profileImage.layer.borderColor = UIColor.white.cgColor
//self.profileImage.setRandomDownloadImage(80, height: 80)
//self.backgroundImage.setRandomDownloadImage(Int(self.frame.size.width), height: 100)
}
#IBAction func followme(_ sender: AnyObject) {
UserDefaults.standard.set(followme.isOn, forKey: "followme")
}
#IBAction func logout(_ sender: AnyObject) {
delegate?.goToNextScene()
print("GOTOSCENE")
}
}
There is are no errors thrown but when I click/tap the button, nothing happens. It just prints "GOTOSCENE".
What I feel is
Your problem is right here
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
I think you have added imageHeaderview from storyboard and in viewdidload you are creating new object of ImageHeaderView and assigning delegate to newly created object.
Try to outlet your ImageHeaderView and assign delegate to outleted object.
Hope this will fix your issue.
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.