How to use instance of API from Main View in Container View? - swift

If i create an instance of mapView in MainView, how can i use that instance in Container View?
class MainView: UIViewController {
var mapView = MapView()
}
class ContainerView: UIViewController {
MainView.mapView.changeCameraPosition()
}
How can i access this mapView instance in container View or is it possible?

The solution i found and also with help from Burnsi and Rob is to pass the instance as an object when adding view controller to container view:
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let nextPageVC = storyboard.instantiateViewController(withIdentifier:"NextPage") as! NextPage
nextPageVC.mapView = mapView
addChild(nextPageVC)
containerView.addSubview(nextPageVC.view)
nextPageVC.view.frame = containerView.bounds
nextPageVC.didMove(toParent: self)
And i also changed the class type of the Container View and Main View in NextPageVC to PassthruView so that users can interact with the map when Container View Controller is on top:
import Foundation
import UIKit
class PassthruView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Get the hit view we would normally get with a standard UIView
let hitView = super.hitTest(point, with: event)
// If the hit view was ourself (meaning no subview was touched),
// return nil instead. Otherwise, return hitView, which must be a subview.
return hitView == self ? nil : hitView
}
}

If Main View has the Container View, then the scenario would be like this if I'm not wrong:
class MainView: UIViewController {
var mapView = MapView()
var containerView = ContainerView()
}
If that's the case, you can make a method inside ContainerView class that takes an object of MapView as a parameter and call that method inside MainView class on containerView (the object of ContainerView that you have in MainView class).
The method could be something like this:
private func changeCameraPosition(mapView: MapView) {
mapView.changeCameraPosition()
}
And call this method on containerView object in Main View class as:
containerView.changeCameraPosition(mapView: mapView)

Related

SnapKit snp can't assign to UIHostingController

On many of projects I use SnapKit. And on new project too. On project I have ViewController which connected with SwiftUI view:
class OfficeListViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let vc = UIHostingController(rootView: OfficeListView())
addChild(vc)
view.addSubview(vc.view)
vc.didMove(toParent: self)
vc.view.translatesAutoresizingMaskIntoConstraints = false
// Here I want to set constraints to vc
vc.snp // throws error: Value of type 'UIHostingController<OfficeView>' has no member 'snp'
}
}
struct OfficeListView: View {
var body: some View {
Text("View")
}
}
But it throws error:
Value of type 'UIHostingController' has no member 'snp'
How to correctly use SnapKit with it?
The UIHostingController is just subclass of UIViewController and it renders SwiftUI view inside regular UIView. If you want to set up constrains, then you should use vc.view as we usually do with views.

how to segue to storyboard viewcontroller from xib view with swift 3

I'm having the hardest time finding an answer for this.
I have a xib view that is within a scrollview that is within a view controller. In the xib I have a button with an action and I need to segue to a view controller I have in my storyboard. I also would like to be able to use a custom segue.
So far, I have read that I can instantiate the viewcontroller from the storyboard to segue to it. But then I don't know how to present that controller.
thanks for any help...
UPDATE:
this is the code I'm using to perform the segue.
In parent ViewController:
static var referenceVC: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("viewdidload")
LevelSelectViewController.referenceVC = self
setupScrollView()
}
code in xib view file
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sightWordController")
let parent = LevelSelectViewController.referenceVC!
let segue = InFromRightCustomSegue(identifier: "test", source: parent, destination: vc)
segue.perform()
As noted in the comments, Segues are typically confined to storyboard usage as noted in the documentation. You can implement a custom xib view in a storyboard via #IBDesignable like approaches and have you're view load from the xib into the storyboard file/class. This way, you gain the benefits of both worlds. Otherwise, you may want to approach this in another fashion (such as delegates/target-action events, etc).
You may also climb the responder chain and call a segue related to the VC loaded from the storyboard (the segue doesn't necessarily have to be attached to any particular action) via getting a reference to the VC and calling the segue. You can climb the responder chain in a manner such as the example code below:
protocol ChildViewControllerContainer {
var parentViewController: UIViewController? { get }
}
protocol ViewControllerTraversable {
func viewController<T: UIViewController>() -> T?
}
extension UIView: ViewControllerTraversable {
func viewController<T: UIViewController>() -> T? {
var responder = next
while let currentResponder = responder {
guard responder is T else {
responder = currentResponder.next
continue
}
break
}
return responder as? T
}
}
extension UITableViewCell: ChildViewControllerContainer {
weak var parentViewController: UIViewController? {
return viewController() as UIViewController?
}
}

Update UITabBarController bar item from NSObject class

I have NSObject class listening for a specific event from my server.
When this specific event happens, I would like to update the badge value of an existing tabBar item from my UITabBarController called TabBarController.
How can I access it from the NSObject class?
Below is the NSOBject class listening for the event.
The function connectedToSocketIo() is launched when the application is launched.
The print("Event is working") is displayed in the terminal so everything is working.
The only thing I need now is to be able to update the badge of a specific bar item.
import Foundation
import UIKit
import SwiftyJSON
class SocketIOManager: NSObject{
func connectedToSocketIo(){
socket.on("post-channel:App\\Events\\contact\\newContactRequest"){ (data, ack) -> Void in
let json = JSON(data)
if json[0]["id"].string! == self.defaults.stringForKey("user_id")! {
print("event is working")
// I want to change the tab bar item badge here
} else {
print("no event")
}
}
}
}
You should try to get a reference to the UITabBarController in your SocketIOManager class. Once you have a reference the tab bar controller you can change the badge value of the desired UITabBarItem.
import Foundation
import UIKit
import SwiftyJSON
class SocketIOManager: NSObject {
/*
var tabBarController: UITabBarController!
*/
// When the tabBarController gets set the connectedToSocketIO function gets automatically called.
var tabBarController: UITabBarController! {
didSet {
connectedToSocketIO()
}
}
init() {
super.init()
}
// Either call this function
init(tabBarController: UITabBarController) {
super.init()
self.tabBarController = tabBarController
connectedToSocketIO()
}
// Or create a setter
func setTabBarController(tabBarController: UITabBarController) {
self.tabBarController = tabBarController
}
func connectedToSocketIo() {
socket.on("post-channel:App\\Events\\contact\\newContactRequest"){ (data, ack) -> Void in
let json = JSON(data)
if json[0]["id"].string! == self.defaults.stringForKey("user_id")! {
print("event is working")
// Set the desired tab bar item to a given value
tabBarController!.tabBar.items![0].badgeValue = "1"
} else {
print("no event")
}
}
}
}
EDIT
class CustomTabBarController: UITabBarController {
var socketIOManager: SocketIOManager!
viewDidLoad() {
super.viewDidLoad()
socketIOManager = SocketIOManager(tabBarController: self)
}
}
Hope this helps!
#Jessy Naus
I removed:
the socket connection from the app delegate,
the override init function inside the socketIOManager so the init(UITabBarController)
and added the socket.connect() (from socket.io library) function inside the init function linked to the tab bar controller as follow:
init(tabBarController: UITabBarController) {
super.init()
self.tabBarController = tabBarController
socket.connect()
self.listeningToSocketEvent()
}
I have replaced "self.connectedToSocketIo()" by "listeningToSocketEvent()" has the meaning of this function is more clear.
All together following your instructions mentioned above = Works perfectly. So I put your answer as the good one.
Really not easy concept. Will still need some time to assimilate it and apply it to other components of the UI.
Thanks a lot for your help on this!
actually, I found another way which avoid touching my socket.io instance.
Source:
Swift 2: How to Load UITabBarController after Showing LoginViewController
I just make the link with my tab bar controller as follow:
In my SocketIOManager
//"MainTabBarController" being the UITabBarController identifier I have set in the storyboard.
//TabBarController being my custom UITabBarController class.
// receivedNotification() being a method defined in my custom TabBarController class
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController: TabBarController = mainStoryboard.instantiateViewControllerWithIdentifier("MainTabBarController") as! TabBarController
tabBarController.receivedNotification(1)
In my TabBarController class:
func receivedNotification(barItem: Int){
if let actualValue = self.tabBar.items![barItem].badgeValue {
let currentValue = Int(actualValue)
self.tabBar.items![barItem].badgeValue = String(currentValue! + 1)
} else {
self.tabBar.items![barItem].badgeValue = "1"
}
// Reload tab bar item
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = self
}

iOS app crashes when tries to add child viewcontroller with mapView inside

I have tabbarController where i put parent viewController with container view inside.
public override func viewDidLoad() {
viewControllers = [
ParentViewController()
]
}
On init i'm initializing 2 child view controllers and adding 1st controller (that does't contain MapView) as child viewController.
At some point of time i need to switch between child controllers, and in that point app crashes
public class ParentViewController: UIViewController {
#IBOutlet weak var containerView: UIView!
let firstChildController: ViewControllerWithoutMapView
let secondChildController: ViewControllerWithMapView
init() {
firstChildController = ViewControllerWithoutMapView()
secondChildController = ViewControllerWithMapView()
super.init(nibName: "ParentViewController", bundle: nil)
}
public override func viewDidLoad() {
firstChildController.view.frame = containerView.bounds
addChildViewController(firstChildController)
firstChildController.willMoveToParentViewController(nil)
containerView.addSubview(firstChildController.view)
firstChildController.didMoveToParentViewController(self)
}
func switchChildControllers() {
secondChildController.view.frame = containerView.bounds <<<<< crash here
.....
}
}
I know about crashes that appears if you're not importing MapKit, i tried to import it everywhere - no luck.
What is the correct way to switch child viewControllers with MapView inside one of it?

Sending data to another view: can't unwrap option

I know that this has to be a simple fix, but can't seem to understand why my code is not working. Basically I am trying to send a value from a text field in 1 view to a 2nd view's label.
ViewController.swift
#IBOutlet var Text1st: UITextField
#IBAction func Goto2ndView(sender: AnyObject) {
let view2 = self.storyboard.instantiateViewControllerWithIdentifier("view2") as MyView2
//view2.Label2nd.text=text;
self.navigationController.pushViewController(view2, animated: true)
}
MyView2.swift
#IBOutlet var Label2nd: UILabel
override func viewDidLoad() {
super.viewDidLoad()
var VC = ViewController()
var string = (VC.Text1st.text) //it doesn't like this, I get a 'Can't unwrap Option.. error'
println(string)
}
-------EDITED UPDATED CODE FROM (drewag)-------
ViewController.swift
let text = "text"
var sendString = Text1st.text
println(sendString) //successfully print it out.
let view2 = self.storyboard.instantiateViewControllerWithIdentifier("view2") as MyView2
view2.Label2nd.text=sendString;
self.navigationController.pushViewController(view2, animated: true)
MyView2.swift
#IBOutlet var Label2nd: UILabel
override func viewDidLoad() {
super.viewDidLoad()
var VC = ViewController()
var string = self.Label2nd.text
println(string) //still getting the error of an unwrap optional.none
}
var VC = ViewController() creates a new instance of ViewController. Unless there is a default value, you are not going to get any value out of VC.Text1st.text. You really should use a string variable on your second view controller to pass the data to it.
Also, a note on common formatting:
Class names should start with a capital letter (as you have)
Method / function names should start with a lower case letter
UIViewController subclasses should have "Controller" included in their name, otherwise, it looks like it is a subclass of UIView which is an entirely different level of Model View Controller (the architecture of all UIKit and Cocoa frameworks)
Edit:
Here is some example code:
class ViewController1 : UIViewController {
...
func goToSecondView() {
var viewController = ViewController2()
viewController.myString = "Some String"
self.navigationController.pushViewController(viewController, animated: true)
}
}
class ViewController2 : UIViewController {
var myString : String?
func methodToUseMyString() {
if let string = self.myString {
println(string)
}
}
...
}
Note, I am not creating ViewController2 using a storyboard. I personally prefer avoiding storyboards because they don't scale well and I find editing them to be very cumbersome. You can of course change it to create the view controller out of the storyboard if you prefer.
jatoben is correct that you want to use optional binding. IBOutlets are automatically optionals so you should check the textfield to see if it is nil.
if let textField = VC.Text1st {
println(textField.text)
}
This should prevent your app from crashing, but it will not print out anything because your text field has not yet been initialized.
Edit:
If you want to have a reference to your initial ViewController inside your second you're going to have to change a few things. First add a property on your second viewcontroller that will be for the first view controller:
#IBOutlet var Label2nd: UILabel //existing code
var firstVC: ViewController? //new
Then after you create view2, set it's firstVC as the ViewController you are currently in:
let view2 = self.storyboard.instantiateViewControllerWithIdentifier("view2") as MyView2 //already in your code
view2.firstVC = self //new
Finally in your viewDidLoad in your second view controller, use firstVC instead of the ViewController you recreated. It will look something like this:
override func viewDidLoad() {
super.viewDidLoad()
if let textField = firstVC?.Text2nd {
println(textField.text)
}
}
Use optional binding to unwrap the property:
if let string = VC.Text1st.text {
println(string)
}