Cannot add viewcontroller´s frame to a scrollview in another viewcontroller - xcode

I want to add a swipe function to my app and swipe between viewcontrollers as in this tutorial: https://www.youtube.com/watch?v=3jAlg5BnYUU
The source code for the tutorial is here: https://www.veasoftware.com/posts/swipe-navigation-in-swift-xcode-7-ios-9-tutorial
In that guide they are using nib/xib files that they create together with the corresponding viewcontrollers that they then use to call for in their ViewController´s constructors.
I have done exact the same and it is wokring as in the tutorial. But I also want use a ViewController that not has a xib file because it is on my storyboard. That viewcontroller is called ViewController2 in my code below and it is the only that does not appear in the scrollview when I swipe. The other two do because I call for the xibName in the constructor call but ViewController2 does not have a xib file since it is on my storyboard.
So how can I add it to the scrollview so it appears when I swipe?
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let vc0 = ViewController0(nibName: "ViewController0", bundle: nil)
self.addChildViewController(vc0)
self.scrollView.addSubview(vc0.view)
vc0.didMoveToParentViewController(self)
let vc1 = ViewController1(nibName: "ViewController1", bundle: nil)
var frame1 = vc1.view.frame
frame1.origin.x = self.view.frame.size.width
vc1.view.frame = frame1
self.addChildViewController(vc1)
self.scrollView.addSubview(vc1.view)
vc1.didMoveToParentViewController(self)
let vc2 = ViewController2()
var frame2 = vc2.view.frame
frame2.origin.x = self.view.frame.size.width
vc2.view.frame = frame2
self.addChildViewController(vc2)
self.scrollView.addSubview(vc2.view)
vc2.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 3, self.view.frame.size.height - 66);
}

Try doing the following:
1) Getting the storyboard object programmatically.
2) Giving the View Controller 2 in your storyboard file a storyboard ID. You can do this by going to your Main.storyboard file and selecting the view controller you wish to give an identifier and then looking in the Identity Inspector (It should be in the under "Identity" section.
3) Instantiating an instance of View Controller 2 with the storyboard method instantiateViewControllerWithIdentifier. This method uses a storyboard ID as it's argument and adding that as the child of your scroll view.
Like so:
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
//1
let mainStoryboard = UIStoryboard.init(name: "Main", bundle: NSBundle.mainBundle())
//3
let vc2 = mainStoryboard.instantiateViewControllerWithIdentifier("<InsertStoryBoardID>") as! ViewController2
self.addChildViewController(vc2)
self.scrollView.addSubview(vc2.view)
vc2.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 3, self.view.frame.size.height - 66);
}
Hope this helps! Feel free to ask questions!

Related

NSScrollView with AutoLayout constrains made window unresponsive to resizing and dragging

In Apple's official Exhibition app demo, there's such usage in viewDidLoad() in the ImageViewerController.swift
/*
Turn on translatesAutoresizingMaskIntoConstraints, Interface Builder
turns this off for all views in a nib. The `NSScrollView` and `NSClipView`
expect to be able to control their documentView via modifying its `frame`.
*/
imageView.translatesAutoresizingMaskIntoConstraints = true
I noticed that in the xib file, the imageView already has constrains set up. When the line of code is commented out, the view still works.
To better understand the code from the demo, I created a minimal project with only a NSScrollView added under the default view controller's view. Here the NSImageView is created programmatically. If the constrains in h and v are activated, the window will be very big and unresponsive to resizing and dragging.
class ViewController: NSViewController {
#IBOutlet weak var scrollView: NSScrollView!
#IBOutlet weak var clipView: NSClipView!
override func viewDidLoad() {
super.viewDidLoad()
let path = "/Library/Desktop Pictures/Snow.jpg"
let url = URL(fileURLWithPath: path, isDirectory: false)
let image = NSImage(contentsOf: url)
let imageView = NSImageView(image: image!)
imageView.translatesAutoresizingMaskIntoConstraints = false
clipView.addSubview(imageView)
let h = NSLayoutConstraint.constraints(
withVisualFormat: "H:|[imageView]|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0),
metrics: nil,
views: ["imageView" : imageView])
let v = NSLayoutConstraint.constraints(
withVisualFormat: "V:|[imageView]|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0),
metrics: nil,
views: ["imageView" : imageView])
clipView.documentView = imageView
// NSLayoutConstraint.activate([h, v].flatMap({$0}))
}
}
My questions are:
Why and how do the constrains in my project cause the problem, while the constrains in the demo don't.
Are the constrains in the official demo necessary?

how i can load viewController instead of xib file

well i want create notification button when the user click it will show for him viewcontroller or something
i found this library
https://github.com/lucaslt89/PopupContainer
the main problem that you can implement from xib file nib file and only UIView
it's possible to load from Controller from Main Storyboard ?
this is the code
#IBAction func showFromXibButtonPressed(sender: AnyObject) {
let xibView = NSBundle.mainBundle().loadNibNamed("XibPopup", owner: nil, options: nil)[0] as! XibPopup
PopupContainer.generatePopupWithView(xibView).show()
}
You can do that by passing the view of the viewcontroller which you can get from the storyboard.
Example
#IBAction func showFromXibButtonPressed(sender: AnyObject) {
let storyboard = UIStoryboard(name: "StoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("myViewController") as! UIViewController
PopupContainer.generatePopupWithView(vc.view).show()
}
Please assign a proper ViewController Identifier in the storyboard and update the same in the code with the proper storybaord name
Updated the code as per question requirement by checking Hunter's format
Assuming your storyboard is named "Main", and you have an initial view controller, this will would load the view from that controller:
#IBAction func showFromXibButtonPressed(sender: AnyObject) {
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
PopupContainer.generatePopupWithView(controller!.view).show()
}
Note, it would be best practice to change the name of the IBAction to showFromViewControllerButtonPressed, but the above name will work.

cocoa - Edit/Design NSView visually

How can I visually design a NSView in Xcode? I'm trying to build a statusBar app with a statusMenu only
As you can see above, the object appear but I can't edit it visually, then it go like this as a result:
I attempted to use a XIB file (only have a NSView, no ViewController), but I can't put the NSView in the XIB into the AppDelegate. I tried another ways and managed to use the XIB file, but then I can't use the Storyboard.
Do you have any idea for this situation?
UPDATE
Thank Max for answer my question. I actually tried that before but failed. I tried it again after your comment and there's still no luck. Here is how I did
I created a ViewController then I change its view to the MenuCustomView (NSView)
This is the code in AppDelegate
let statusBar = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
var menuCustomView = NSView()
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
let statusIcon = NSImage(named: "statusIcon")
let statusMenu = NSMenu()
statusBar.image = statusIcon
statusBar.menu = statusMenu
let menuItem = NSMenuItem()
menuItem.title = "title"
var vc = ViewController()
menuCustomView = vc.view
menuItem.view = menuCustomView
statusMenu.addItem(menuItem)
}
Files I have in the project
Even though you don't need the view controller, you can use them to design the views. Create one in the storyboard, edit the view the way you want.
Then you can use the view by creating a viewController out of the storyboard and using its view property to attach the view to the NSStatusBarItem
My words in code:
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let viewController = storyboard?.instantiateControllerWithIdentifier("MyViewController") as! NSViewController
let view = viewController.view
I assume you want to add functioning code to the view. To, for example respond to the button click, you have to subclass the NSViewController and then implement the code you would have added to the AppDelegate.
If you need any more explanation, feel free to write me :)

Display a view for few seconds (custom push from NavController)

I started with Master-Detail Application. Now I try to add a custom View (default UIViewController with simple Label) which will be displayed for some seconds (like a loading view) before detail/master views will be accessible.
My Problem now is that if I add my ViewController to AppDelegate and push to it from NavigationController i get an empty view.
See here:
I didn't change anything but adding just one line of code to appdelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self
let masterNavigationController = splitViewController.viewControllers[0] as UINavigationController
let controller = masterNavigationController.topViewController as MasterViewController
controller.managedObjectContext = self.managedObjectContext
// +++ Custom ViewController is to be displayed from here +++
navigationController.pushViewController(ViewController(), animated: true)
return true
}
I do not get where the problem is...
With this line in your code
navigationController.pushViewController(ViewController(), animated: true)
you are instantiating a new, empty view controller, whis is probably not what you want.
Get a reference to your storyboard (i.e from your masterviewcontroller) to create the view controller and push this one on the navigation stack. Example:
if let myVC = controller.storyboard!.instantiateViewControllerWithIdentifier(...) {
navigationController.pushViewController(myVC, animated: true)
}

How to present a modal atop the current view in Swift

(Xcode6, iOS8, Swift, iPad)
I am trying to create a classic Web-like modal view, where the outside of the dialog box is "grayed-out." To accomplish this, I've set the alpha value of the backgroundColor of the view for the modal to 0.5, like so:
self.view.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.5)
The only problem is that when the modal becomes full-screen, the presenting view is removed. (Ref Transparent Modal View on Navigation Controller).
(A bit irritated at the concept here. Why remove the underlying view? A modal is, by definition, to appear atop other content. Once the underlying view is removed, it's not really a modal anymore. it's somewhere between a modal and a push transition. Wa wa wa... Anyway..)
To prevent this from happening, I've set the modalPresentationStyle to CurrentContext in the viewDidLoad method of the parent controller, and in Storyboard... but no luck.
self.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
self.navigationController.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
How do I prevent the presenting view from being removed when the modal becomes full screen?
tyvm.. more info below.
Also in Storyboard, like so (Presentation: Current Context)
Thx for your help... documentation below:
First, remove all explicit setting of modal presentation style in code and do the following:
In the storyboard set the ModalViewController's modalPresentation style to Over Current context
Check the checkboxes in the Root/Presenting ViewController - Provide Context and Define Context.
They seem to be working even unchecked.
You can try this code for Swift:
let popup : PopupVC = self.storyboard?.instantiateViewControllerWithIdentifier("PopupVC") as! PopupVC
let navigationController = UINavigationController(rootViewController: popup)
navigationController.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
self.presentViewController(navigationController, animated: true, completion: nil)
For swift 4 latest syntax using extension:
extension UIViewController {
func presentOnRoot(`with` viewController : UIViewController){
let navigationController = UINavigationController(rootViewController: viewController)
navigationController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
self.present(navigationController, animated: false, completion: nil)
}
}
How to use:
let popup : PopupVC = self.storyboard?.instantiateViewControllerWithIdentifier("PopupVC") as! PopupVC
self.presentOnRoot(with: popup)
The only problem I can see in your code is that you are using CurrentContext instead of OverCurrentContext.
So, replace this:
self.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
self.navigationController.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
for this:
self.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
self.navigationController.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
This worked for me in Swift 5.0. Set the Storyboard Id in the identity inspector as "destinationVC".
#IBAction func buttonTapped(_ sender: Any) {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let destVC = storyboard.instantiateViewController(withIdentifier: "destinationVC") as! MyViewController
destVC.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
destVC.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(destVC, animated: true, completion: nil)
}
The problem with setting the modalPresentationStyle from code was that you should have set it in the init() method of the presented view controller, not the parent view controller.
From UIKit docs: "Defines the transition style that will be used for this view controller when it is presented modally. Set
this property on the view controller to be presented, not the presenter. Defaults to
UIModalTransitionStyleCoverVertical."
The viewDidLoad method will only be called after you already presented the view controller.
The second problem was that you should use UIModalPresentationStyle.overCurrentContext.
The only way I able to get this to work was by doing this on the presenting view controller:
func didTapButton() {
self.definesPresentationContext = true
self.modalTransitionStyle = .crossDissolve
let yourVC = self.storyboard?.instantiateViewController(withIdentifier: "YourViewController") as! YourViewController
let navController = UINavigationController(rootViewController: yourVC)
navController.modalPresentationStyle = .overCurrentContext
navController.modalTransitionStyle = .crossDissolve
self.present(navController, animated: true, completion: nil)
}
I am updating a simple solution. First add an id to your segue which presents modal. Than in properties change it's presentation style to "Over Current Context". Than add this code in presenting view controller (The controller which is presenting modal).
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let Device = UIDevice.currentDevice()
let iosVersion = NSString(string: Device.systemVersion).doubleValue
let iOS8 = iosVersion >= 8
let iOS7 = iosVersion >= 7 && iosVersion < 8
if((segue.identifier == "chatTable")){
if (iOS8){
}
else {
self.navigationController?.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
}
}
}
Make sure you change segue.identifier to your own id ;)

Resources