试图从不同的Swift类调用方法

时间:2017-01-20 18:19:10

标签: swift xcode uitabbarcontroller

我有两个swift类,在一个中,我需要引用另一个swift类中的方法。两个相应的视图控制器都附加到tabBarController,因此我尝试创建这样的实例:

let mapVC : MapVC = self.tabBarController!.viewControllers![1] as! MapVC
mapVC.loadViewIfNeeded()
mapVC.add(newLocation:location_one) // .add is the method I want to call

其中MapVC包含我想要调用的方法。并且mapViewtabBar上的第二个选项,因此索引为1.但是,当我转换对象[fatal error: unexpectedly found nil while unwrapping an Optional value]时,我收到错误mapVC。我认为问题与创建实例mapVC有关,我相信我可能没有正确设置它,但我不确定如何解决它。

我不太确定如何表达我的问题所以如果您仍然感到困惑,我可以在评论中提供更多信息。

4 个答案:

答案 0 :(得分:3)

我通常使用自定义tabBarController作为每个标签的委托来执行此操作,但我非常喜欢您访问堆栈中viewControllers的方法。让我们弄清楚你的版本有什么问题。

尝试像这样运行

if let tC = self.tabBarController {
    if let vcs = tC.viewControllers {
        if let map = vcs[1] as? MapVC {
            print("map vc found in stack")
            map.loadViewIfNeeded()
            map.add(newLocation:location_one)
        } else {
            print("no map vc found at index 1")
        }
    } else {
        print("no stack found")
    }
} else {
    print("no tab bar found")
}

这可能有助于我们使用您的代码调试最新情况,您确定mapVC正在返回nil吗?

修改:正如您在评论中所述,如果您偏离tabBarController,那么新呈现的viewController的tabBarController属性将为零每Apple Docs

  

如果视图控制器未嵌入标签栏控制器中,则此属性为nil。

根据此图,一种解决方案是将tabBarController的引用从vc3传递给vc2

[tabBarController]
    |       |
  [vc1]   [vc2]  - both have a reference to tabBarController
            |
          [vc3]  - no reference yet

设置vc3以保留tabBar参考 - 最好为此创建一个新的var,而不是使用现有的tabBarController属性。

class vc3:UIViewController {

    var tabBarReference:UITabBarController?

    func addALocation(location:CLLocationCoordinate2D) {
        if let tbc = self.tabBarReference {
            if let map = tbc.viewControllers[1] as? MapVC {
                map.loadViewIfNeeded()
                map.add(newLocation:location)
            }
        }
    }

}

然后在vc2内使用prepare for segue方法访问vc3并在呈现之前为其提供参考。

class vc2:UIViewController {

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc3 = segue.destinationViewController as? vc3
            vc3.tabBarReference = self.tabBarController
        }
    }

    func goToVC3() {
        self.performSegueWithIdentifier("goToVC3", sender: nil)
    }

}

我没有测试过这段代码,但它应该在理论上有用,让我知道你是怎么做的。

答案 1 :(得分:1)

我会首先将这个安全数组扩展添加到您的项目中......

extension Collection where Indices.Iterator.Element == Index {

    /// Returns the element at the specified index iff it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

然后......我会检查你的视图控制器是否像这样......

guard let mapVC : MapVC = self.tabBarController?.viewControllers?[safe: 1] as? MapVC else {
   print("could not find mapVC")
   return
}
mapVC.loadViewIfNeeded()
mapVC.add(newLocation:location_one)

有可能你很快就会调用这种方法。听起来像标签栏或控制器没有加载。尝试在viewWillAppear或viewDidAppear中调用作为最后的手段。

答案 2 :(得分:0)

实现此类目标的两种方法是通过委托或通知。在您的情况下,我认为您应该查看通知并在通知后调用add(newLocation:)函数。

下面是一个类在另一个类中调用函数的快速示例。你可以将它扔进游乐场,看看它是如何工作的。

对于视图控制器,您通常会开始观察viewDidLoad上的事件,但根据您的课程结构,其他地方可能更适合。

class MapVC {

    init() {
        print("Adding self as observer now...")
        NotificationCenter.default.addObserver(self, selector: #selector(add), name: NSNotification.Name.init("addNewLocation"), object: nil)
    }

    @objc func add(notification: Notification) {
        guard let location = notification.object as? CLLocation else {
            return print("Did not recieve a CLLocation object in notification")
        }
        print("Got location with lat: \(location.coordinate.latitude), and lon:\(location.coordinate.longitude)")
        // your code for handling the addition of a new location
    }
}

class OtherVC {
    func add() { // however you're calling this function... you might be passing in a location here, but for demonstration, just creating a new one
        let location = CLLocation(latitude: 100, longitude: 100)
        NotificationCenter.default.post(name:     NSNotification.Name.init("addNewLocation"), object: location)
    }
}


let mapVC = MapVC()
let otherVC = OtherVC()

otherVC.add()

答案 3 :(得分:0)

您可以使用NotificationCenter执行此操作。 您需要在要接收通知的类中注册通知侦听器并执行操作。然后,您需要发布其他类的通知。你可以这样做:

 // Define identifier
let notificationName = Notification.Name("MyNotification")

// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(methodToCallWhenReceivingNotification), name: notificationName, object: nil)

// Post notification
NotificationCenter.default.post(name: notificationName, object: nil)

通常,您应该在要接收它的类的viewDidLoad()中注册通知。