Adding function to sidebar menu on Swift - xcode

So I just did a tutorial on adding a blurry sidebar menu. I replicated the code but I cant seem to find out how to call my other ViewControllers I made in storyboard mode.
I have created files and linked them to UIViewControllers. But when I try to call that ViewController with my sidebar menu I'm getting a black screen.
Here is the link of the files I used to follow along http://goo.gl/ULWxJh
And here is the link of the youtube video I followed along with http://www.youtube.com/watch?v=qaLiZgUK2T0
Not sure why! Any help would be greatly appreciated
class ViewController: UIViewController, SideBarDelegate {
var sideBar:SideBar = SideBar() // Side Bar
override func viewDidLoad() {
super.viewDidLoad()
// Side bar action and text
sideBar = SideBar(sourceView: self.view, menuItems: ["Home", "Business Directory", "Classifieds", "Featured News", "Jobs", "Restaurants", "Sports"])
sideBar.delegate = self
}
// Side Bar funcion
func sideBarDidSelectButtonAtIndex(index: Int) {
if index == 0{
let vc = ViewController()
self.presentViewController(vc, animated: true, completion: nil)
}else if index == 1{
let vc = businessDirectoryVC()
self.presentViewController(vc, animated: true, completion: nil)
}
}
}

Add a Storyboard ID (in this case "view") to the View Controller in your Main Storyboard and then add the following to the file from where you want to call the sidebar from:
func sideBarDidSelectButtonAtIndex(index: Int) {
if index == 0 {
let storyBoard:UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let showView = storyBoard.instantiateViewControllerWithIdentifier("view") as! ViewController
self.presentViewController(showUebersicht, animated:true, completion:nil)
}
}
Value "0" stands for the first item in the sidebar. Change the value for another value of the array where the items of the sidebar are stored.

Related

Passing data between view controllers with a UIAlertAction in Swift

I have a tableView, and when the user taps a cell, it opens an alert box with the options "Close" and "Search". The handler for "Search" is shown below as func searchSongAction.
This presents a new viewcontroller (embedded in a Nav Bar) that searches the song online. In this new viewcontroller, Search, there are 2 functions: one searches the song currently playing and the other searches the song that the user asked to search from the tableView alert.
I am trying to pass the data from the cell into the Search class, but I keep coming up short. I feel like what I have is correct, but that is obviously not he case.
Any ideas?
Ask me if you need any more information.
History.swift
func searchSongAction(alert: UIAlertAction!) {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("Search") as! Search
let navigationController = UINavigationController(rootViewController: vc)
self.presentViewController(navigationController, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationNavigationController = segue.destinationViewController as! UINavigationController
let targetController = destinationNavigationController.topViewController as! Search
targetController.searchType = "Previous"
targetController.songNowText = self.songToSearch
targetController.artistNowText = self.artistToSearch
}
Search.swift
class Search: UIViewController {
var songNowText = ""
var artistNowText = ""
var searchType = ""
override func viewDidLoad() {
super.viewDidLoad()
if searchType == "Previous" {
searchSongPrevious()
} else {
searchSongNow()
}
}
}
You're not actually performing a Segue from your searchSongAction: function, so I'm guessing that prepareForSegue:sender: isn't actually being called and therefore the setup is not running.
Try assigning searchType, songNowText and artistNowText on vc inside of searchSongAction::
func searchSongAction(alert: UIAlertAction!) {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("Search") as! Search
//Setup the properties
vc.searchType = "Previous"
vc.songNowText = self.songToSearch
vc.artistNowText = self.artistToSearch
let navigationController = UINavigationController(rootViewController: vc)
self.presentViewController(navigationController, animated: true, completion: nil)
}
As a side note, typically you'll want to call super's implementation of a function when overriding it (you have neglected to do this in prepareForSegue:sender:)

Why the UIButton is so big in a simulator?

I have a UIPageViewController and a UIButton under it. Here is the screenshot of my storyboard.
When I build the app, the button is huge:
All of my constraints were set automatically. I tried to specify the height, but it doesn't help. Any ideas?
P.S. I'm using XCode 6.3.
Edit:
ViewController.swift:
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
#IBOutlet weak var restartButton: UIButton!
var pageViewController: UIPageViewController!
var pageTitles: NSArray!
var pageImages: NSArray!
override func viewDidLoad() {
super.viewDidLoad()
self.pageTitles = NSArray(objects: "Page 1", "Page 2")
self.pageImages = NSArray(objects: "algorithm", "apoint")
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.size.height - restartButton.frame.height)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
self.view.sendSubviewToBack(self.pageViewController.view)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func viewControllerAtIndex(index: Int) -> ContentViewController {
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
return ContentViewController()
}
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.imageFile = self.pageImages[index] as! String
vc.titleText = self.pageTitles[index] as! String
vc.pageIndex = index
return vc
}
#IBAction func restartAction(sender: AnyObject) {
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == 0) || (index == NSNotFound) {
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if index == NSNotFound {
return nil
}
index++
if index == self.pageTitles.count {
return nil
}
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return self.pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
I'm going to guess that the problem is that you have a constraint from the top of the button to something else in the interface. Get rid of that constraint. The only constraints you need for a button at the bottom of the screen are its bottom and its right-or-left-or-center - its width and height are automatic.
You'll want to find your button in your Controller Scene like this, highlight your constraints and delete them.
Personally when I have constraint issues, I start from scratch by deleting all of the constraints across the board. This is just my personal approach.
Next thing I do is start with the item at the top of my storyboard view and set its constraints like this:
Notice in my add constraints window i am only selecting the top and left margins and the width and height. I do this to each of my objects starting from the top of the screen and working my way to the bottom.
Obviously you'll need to play with this feature a bit to get your desired results. Please note that what I've provided is just an example and not a fix-all.
edit:
after reading your comment I am not sure this solution will help you, I didn't realize that you made your button programmatically. I'm going to leave it up for the time being.

Resizing NSWindow to match view controller size in storyboard

I am working on Xcode 6.1.1 on OSX 10.10. I am trying out storyboards for Mac apps. I have a NSTabViewController using the new NSTabViewControllerTabStyleToolbar tabStyle and it is set as the default view controller for the window controller. How do I make my window resize according to the current selected view controller?
Is it possible to do entirely in Interface Builder?
Here is what my storyboard looks like:
The auto layout answer is half of it. You need to set the preferredContentSize in your ViewController for each tab to the fitting size (if you wanted the tab to size to the smallest size satisfying all constraints).
override func viewWillAppear() {
super.viewWillAppear()
preferredContentSize = view.fittingSize
}
If your constraints are causing an issue below try first with a fixed size, the example below sets this in the tab item's view controller's viewWillAppear function (Swift used here, but the Objective-C version works just as well).
override func viewWillAppear() {
super.viewWillAppear()
preferredContentSize = NSSize(width: 400, height: 280)
}
If that works, fiddle with your constraints to figure out what's going on
This solution for 'toolbar style' tab view controllers does animate and supports the nice crossfade effect. In the storyboard designer, add 'TabViewController' in the custom class name field of the NSTabViewController. Don't forget to assign a title to each viewController, this is used as a key value.
import Cocoa
class TabViewController: NSTabViewController {
private lazy var tabViewSizes: [String : NSSize] = [:]
override func viewDidLoad() {
// Add size of first tab to tabViewSizes
if let viewController = self.tabViewItems.first?.viewController, let title = viewController.title {
tabViewSizes[title] = viewController.view.frame.size
}
super.viewDidLoad()
}
override func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions, completionHandler completion: (() -> Void)?) {
NSAnimationContext.runAnimationGroup({ context in
context.duration = 0.5
self.updateWindowFrameAnimated(viewController: toViewController)
super.transition(from: fromViewController, to: toViewController, options: [.crossfade, .allowUserInteraction], completionHandler: completion)
}, completionHandler: nil)
}
func updateWindowFrameAnimated(viewController: NSViewController) {
guard let title = viewController.title, let window = view.window else {
return
}
let contentSize: NSSize
if tabViewSizes.keys.contains(title) {
contentSize = tabViewSizes[title]!
}
else {
contentSize = viewController.view.frame.size
tabViewSizes[title] = contentSize
}
let newWindowSize = window.frameRect(forContentRect: NSRect(origin: NSPoint.zero, size: contentSize)).size
var frame = window.frame
frame.origin.y += frame.height
frame.origin.y -= newWindowSize.height
frame.size = newWindowSize
window.animator().setFrame(frame, display: false)
}
}
The window containing a toolbar style tab view controller does resize without any code if you have auto layout constraints in your storyboard tab views (macOS 11.1, Xcode 12.3). I haven't tried other style tab view controllers.
If you want to resize with animation as in Finder, it is sufficient to add one override in your tab view controller. It will resize the window with system-calculated resize animation time and will hide the tab view during resize animation:
class PreferencesTabViewController: NSTabViewController {
override func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions = [], completionHandler completion: (() -> Void)? = nil) {
guard let window = view.window else {
super.transition(from: fromViewController, to: toViewController, options: options, completionHandler: completion)
return
}
let fromSize = window.frame.size
let toSize = window.frameRect(forContentRect: toViewController.view.frame).size
let widthDelta = toSize.width - fromSize.width
let heightDelta = toSize.height - fromSize.height
var toOrigin = window.frame.origin
toOrigin.x += widthDelta / 2
toOrigin.y -= heightDelta
let toFrame = NSRect(origin: toOrigin, size: toSize)
NSAnimationContext.runAnimationGroup { context in
context.duration = window.animationResizeTime(toFrame)
view.isHidden = true
window.animator().setFrame(toFrame, display: false)
super.transition(from: fromViewController, to: toViewController, options: options, completionHandler: completion)
} completionHandler: { [weak self] in
self?.view.isHidden = false
}
}
}
Please adjust closure syntax if you are using Swift versions older than 5.3.
Use autolayout. Set explicit size constraints on you views. Or once you have entered the UI into each tab view item's view set up the internal constraints such that they force view to be the size you want.

How can I call a function/action when a statusItem is clicked?

I have this code that opens up a popover element at the "sender" location, i.e. the button that was pressed. How can I make this function call when a statusItem is clicked, so that the popover comes down from the status/menu bar?
#IBAction func togglePopover(sender: AnyObject) {
if !(popoverIsOpen) {
myPopover.showRelativeToRect(sender.bounds, ofView: popoverButton, preferredEdge: NSRectEdge(3))
popoverIsOpen = true
}
else {
myPopover.close()
popoverIsOpen = false
}
}
I am currently using NSPopover and NSStatusItem.
edit: The changelog for Xcode 6 beta 4 added NSStatusItem.button and softly deprecated the previous form of calls like NSStatusItem.action, NSStatusItem.title, NSStatusItem.target, etc.
The documentation now reads
NSStatusItem.button
The button that is displayed in the status bar. This is created automatically on the creation of the StatusItem. Behavior customization for the button, such as image, target/action, tooltip, can be set with this property.
I was able to reach an implementation shown below, using the new NSStatusBarButton visual representation of an NSStatusBarItem. In this example, my .xib file has the NSPopover element already connected to a view, which isn't shown here.
#IBOutlet weak var myPopover: NSPopover!
var statusBar: NSStatusItem!
var popoverIsOpen = false
#IBAction func togglePopover(sender: AnyObject) {
if !(popoverIsOpen) {
myPopover.showRelativeToRect(sender.bounds, ofView: statusBar.button, preferredEdge: NSRectEdge(3))
popoverIsOpen = true
}
else {
myPopover.close()
popoverIsOpen = false
}
}
func applicationDidFinishLaunching(aNotification: NSNotification?) {
//initialize menu bar icon
statusBar = NSStatusBar.systemStatusBar().statusItemWithLength(CGFloat(48))
statusBar.button.title = "Your App Title"
statusBar.button.appearsDisabled = false
statusBar.button.action = Selector("togglePopover:")
statusBar.button.target = self
}

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