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

尝试从不同的Swift类调用方法,swift,xcode,uitabbarcontroller,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

我有两个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
包含我希望调用的方法。而
地图视图
选项卡
上的第二个选项,因此索引为1。但是,当我强制转换对象
mapVC
时,我得到了错误
[致命错误:在展开可选值时意外发现nil]
。我相信这个问题与创建一个实例
mapVC
有关,我相信我可能没有正确设置它,但我不确定如何修复它


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

实现这一点的两种方法是通过委派或通知。在您的情况下,我认为您应该查看通知并在收到通知后调用
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()

您可以使用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()中注册要接收通知的类的通知。

我通常使用自定义的
tabBarController
作为每个选项卡的委托,但我非常喜欢您访问堆栈中
ViewController
的方法。让我们看看你的版本有什么问题

试着像这样运行它

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
属性将根据设置为零

如果视图控制器未嵌入选项卡栏控制器中,则此属性为nil

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

[tabBarController]
    |       |
  [vc1]   [vc2]  - both have a reference to tabBarController
            |
          [vc3]  - no reference yet
设置
vc3
以保持选项卡栏引用
-最好为此创建一个新的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)
    }

}

我还没有测试过这段代码,但它在理论上应该可以工作,让我知道你进展如何。

我会先将这个安全阵列扩展添加到你的项目中

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)

很可能您调用此方法的时间太早了。听起来好像选项卡栏或控制器未加载。作为最后手段,请尝试在ViewWillDisplay或ViewDidDisplay中打电话。

您能进一步解释什么是通知以及我如何实现此功能吗?@Kevin我已编辑了我的回复,其中包括一个示例,您可以在操场上进行测试。我喜欢通过通知传递信息的概念。您的代码已经在操场上运行了,但是当我尝试将其实现到我的文件中时,它给了我一系列错误(必需的初始值设定项init(coder)必须由UIViewController的子类提供)。与操场相比,实施方式不同吗?@Kevin正确,这是不同的。我刚刚制作了两个类,而不是视图控制器。正如我在编辑中所说,如果您没有使用视图控制器进行任何奇特的初始化,您应该在
viewdiload
中移动寄存器代码。我在代码中实现了这一点,它返回“找不到选项卡栏”。这就是我所认为的问题所在,但这很奇怪,因为我的视图控制器是在tabBarController中构造的。你知道怎么回事吗?@Kevin你是如何将ViewController添加到选项卡栏的?我设置选项卡栏的方式是最初使用默认设置(2个选项卡),然后我在底部添加了一个选项卡项,该选项卡项与第三个视图控制器相连。这回答了你的问题吗?如果你需要更多的信息,我很乐意提供!我认为我的问题是,我正在使用的viewController底部没有选项卡栏。因此,我试图将信息从常规的viewController发送到tabBarController。我已经更新了答案,我认为Swift 3具有不同的prepare for segue方法实现。。可能值得自动填充以确保其正确。。你可以随意命名你的序列。