How should I add tab view in swift os x - xcode

How should I add a tab view and switch tab view in tabViewController ?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let newItem: NSTabViewItem = NSTabViewItem(identifier: "abc")
let tv: NSTextView = NSTextView(frame: NSZeroRect)
newItem.view?.autoresizesSubviews = true
newItem.view?.addSubview(tv)
newItem.label = "Untitled"
//tabView.addTabViewItem(newItem)
//tabView.selectTabViewItem(newItem)
//tabView.insertTabViewItem(newItem, atIndex: 3)
//tabView.selectFirstTabViewItem(IspViewController)
}

We should set NSViewController to created NSTabViewItem.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let newItem: NSTabViewItem = NSTabViewItem(identifier: "abc")
newItem.label = "Untitled"
// "tvcontroller" is in storyboard
newItem.viewController = storyboard?.instantiateControllerWithIdentifier("tvcontroller") as? NSViewController
addTabViewItem(newItem)
}

Related

Border Doesn't Draw in NSView When inside NSTableCellView

I have three NSViews inside an NSTableCellView. Depending on the data for the row, I want to show a selected role with a border on the NSView like this:
The blue border NSView is a subclass that looks like this:
class RolePill: NSView{
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
layer?.cornerRadius = 9
layer?.borderWidth = 2
layer?.borderColor = NSColor.clear.cgColor
}
}
The role gets set initially when my table loads like this:
extension UsersVC: NSTableViewDelegate, NSTableViewDataSource{
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "UserCell"), owner: nil) as! UserCell
let user = users[row]
//Role
cell.setRole(role: user.role)
}
}
And my table cell, where the role gets set on load and on a button click, is set up like this:
class UserCell: NSTableCellView{
#IBOutlet weak var wrapOwner: RolePill!
#IBOutlet weak var wrapAdmin: RolePill!
#IBOutlet weak var wrapUser: RolePill!
#IBAction func clickOwner(_ sender: NSButton) {
setRole(role: "owner")
}
#IBAction func clickAdmin(_ sender: NSButton) {
setRole(role: "admin")
}
#IBAction func clickUser(_ sender: NSButton) {
setRole(role: "user")
}
func setRole(role: String){
let selectedColor = getAccentColor()
let offColor = Color(named: "BravoDark")!
let offTextColor = Color(named: "BFC0C2")
switch role{
case "owner":
wrapOwner.layer?.borderColor = selectedColor.cgColor
wrapAdmin.layer?.borderColor = offColor.cgColor
wrapUser.layer?.borderColor = offColor.cgColor
labelOwner.textColor = Color.white
labelAdmin.textColor = offTextColor
labelUser.textColor = offTextColor
case "admin":
wrapOwner.layer?.borderColor = offColor.cgColor
wrapAdmin.layer?.borderColor = selectedColor.cgColor
wrapUser.layer?.borderColor = offColor.cgColor
labelOwner.textColor = offTextColor
labelAdmin.textColor = Color.white
labelUser.textColor = offTextColor
default:
wrapOwner.layer?.borderColor = offColor.cgColor
wrapAdmin.layer?.borderColor = offColor.cgColor
wrapUser.layer?.borderColor = selectedColor.cgColor
labelOwner.textColor = offTextColor
labelAdmin.textColor = offTextColor
labelUser.textColor = Color.white
}
}
}
When the table loads initially, and anytime it refreshes (tableView.reloadData()) I lose my border and it looks like this:
As you can see, the textColor is set correctly to white. But for some reason, the border isn't set until I actually click on one of the IBAction buttons and manually trigger a change.
I suspect this is some kind of layer drawing bug where the RolePill class is redrawing every time my NSTableView reloads, but I don't know how to get it to accept the initial role state sent in tableView viewForRow.
Any idea what I'm doing wrong here? Thanks!
I was able to get this to work by setting all the NSView properties inside setRole() like this:
view.wantsLayer = true
view.layer?.cornerRadius = 11
view.layer?.borderWidth = 2
view.layer?.borderColor = getAccentColor().cgColor
label.textColor = Color.white
It seemed that the draw() method on my RolePill subclass was getting called frequently, so I couldn't set a border color in there since it doesn't know what the user's data state is.

Additional view in NSCollectionViewItem pauses dragEvent in CollectionViewController

I am trying to implement drop delegates on a NSCollectionViewController and having issues using a custom NSCollectionViewItem with an additional View Layer I've added onto the CollectionView Item. FWIW, The additional view is used draw a dashed border to indicate a drop area.
The drag event works fine on this collectionItem, and all other collectionItems without this view when it is hidden, but as soon as the drag event occurs on top of this view, the drag event pauses.
The drag event resumes as soon as the mouse is dragged outside of the view, but nothing happens if I release the drag while the mouse is over the view.
I would love to know what is happening here and how to prevent the custom view from "stealing" the mouse event from the CollectionViewContoller.
Delegate Method on DropViewController
func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
print("1")
if proposedDropIndexPath.pointee.item <= self.destinationDirectoryArray.count {
if proposedDropOperation.pointee == NSCollectionView.DropOperation.on {
return .move
}
} else if proposedDropIndexPath.pointee.item == self.destinationDirectoryArray.count {
//There's some stuff here validating the URL removed for brevity. It works okay when the focus is outside the view, but happy to add back in if helpful
if proposedDropOperation.pointee == NSCollectionView.DropOperation.on {
return .move
}
}
return[]
}
Configuring Collection View
func configureCollectionView() {
let flowLayout = NSCollectionViewFlowLayout()
flowLayout.minimumInteritemSpacing = 8.0
flowLayout.minimumLineSpacing = 8.0
destinationCollectionView.delegate = self
destinationCollectionView.dataSource = self
destinationCollectionView.register(NSNib(nibNamed: "DestinationCollectionItem", bundle: nil), forItemWithIdentifier: directoryItemIdentifier)
destinationCollectionView.collectionViewLayout = flowLayout
destinationCollectionView.registerForDraggedTypes([.fileURL])
destinationCollectionView.setDraggingSourceOperationMask(NSDragOperation.move, forLocal: true)
}
Collection View Item Setup
class DestinationCollectionItem: NSCollectionViewItem {
#IBOutlet weak var backgroundLayer: NSView!
override func viewDidLoad() {
super.viewDidLoad()
self.highlightState = .none
view.wantsLayer = true
view.layer?.cornerRadius = 8.0
backgroundLayer.isHidden = true
}
}
Custom Border View - Applied custom class in Xib and linked to File's Owner
class BorderedView: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let path : NSBezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 10.0, yRadius: 10.0)
path.addClip()
let dashHeight: CGFloat = 2
let dashLength: CGFloat = 7
let dashColor: NSColor = .lightGray
// setup the context
let currentContext = NSGraphicsContext.current!.cgContext
currentContext.setLineWidth(dashHeight)
currentContext.setLineDash(phase: 0, lengths: [dashLength])
currentContext.setStrokeColor(dashColor.cgColor)
// draw the dashed path
let cgPath : CGPath = CGPath(roundedRect: NSRectToCGRect(self.bounds), cornerWidth: 10.0, cornerHeight: 10.0, transform: nil)
currentContext.addPath(cgPath)
currentContext.strokePath()
}
}
Well - I solved this one pretty quick.
While I previously tried adding unregisterDraggedTypes() to the backgroundLayer, the issue turned out to also be occurring on the image layer. I applied it to both the Image and backgroundLayer and it works now.
Collection View Item Setup
class DestinationCollectionItem: NSCollectionViewItem {
#IBOutlet weak var backgroundLayer: NSView!
override func viewDidLoad() {
super.viewDidLoad()
self.highlightState = .none
view.wantsLayer = true
view.layer?.cornerRadius = 8.0
backgroundLayer.isHidden = true
backgroundLayer.unregisterDraggedTypes()
self.imageView?.unregisterDraggedTypes()
self.textField?.unregisterDraggedTypes()
}
}

Displaying search bar in navigation bar in iOS 8

UISearchDisplayController had a boolean property called displaysSearchBarInNavigationBar. What's the equivalent in iOS 8 to have my search bar move up there? Any guidance is greatly appreciated.
Here's my code, I'm not entirely sure why this isn't working. When I click the search bar, it just disappears instead of moving itself and the navigation bar up.
import UIKit
class ViewController: UIViewController, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource {
let searchController = UISearchController(searchResultsController: nil)
var tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = true
self.searchController.dimsBackgroundDuringPresentation = true
tableView.dataSource = self
tableView.delegate = self
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = true
self.setupSearchBar()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
func setupSearchBar() {
// Set search bar position and dimensions
var searchBarFrame: CGRect = self.searchController.searchBar.frame
var viewFrame = self.view.frame
self.searchController.searchBar.frame = CGRectMake(searchBarFrame.origin.x, searchBarFrame.origin.y + 64,viewFrame.size.width, 44)
// Add search controller's search bar to our view and bring it to forefront
self.view.addSubview(self.searchController.searchBar)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
}
According to Apple :
UISearchDisplayController is deprecated in iOS 8. (Note that UISearchDisplayDelegate is also deprecated.) To manage the presentation of a search bar and display search results in iOS 8 and later, instead use UISearchController.
The UISearchController class defines an interface that manages the presentation of a search bar in concert with the search results controller’s content. The search results controller, a UIViewController object specified by the searchResultsController property, manages the results of the search.
Now you can use the UISearchController to show the search bar in your navigation bar in the following way:
class ViewController: UIViewController, UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate {
var searchController : UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.dimsBackgroundDuringPresentation = true
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = true
}
func updateSearchResults(for searchController: UISearchController) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
But you have to consider you need to set a UINavigationController like in this Storyboard :
You can do it very easy just select your ViewController and see the following steps:
And then you should see in your device when you make click in the search bar the following picture:
I hope this help you

How to open a new window with its own ViewController from AppDelegate in Swift

I have made a statusBar application with a drop down. I would like to open a settingsWindow from that dropdown. I have made the settings window with its own ViewController.
The issue is that i can't figure out how to instantiate and show the settingsWindow that i have made. I have tried to follow every thread on the internet without any success.
My Viewcontroller:
class SettingsViewController: NSViewController {
#IBOutlet var ipAddress: NSTextField!
#IBOutlet var port: NSTextField!
#IBAction func connect(sender: AnyObject) {}
override func viewDidLoad() {
super.viewDidLoad()
}
}
My AppDelegate:
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet var statusMenu: NSMenu!
var statusItem: NSStatusItem?
var tcpService: TcpService = TcpService()
func applicationDidFinishLaunching(aNotification: NSNotification?) {
let bar = NSStatusBar.systemStatusBar()
statusItem = bar.statusItemWithLength(20)
statusItem!.menu = statusMenu
statusItem!.image = NSImage(byReferencingFile: NSBundle.mainBundle().pathForResource("16*16", ofType: "png"))
statusItem!.highlightMode = true
tcpService.initOutputStream("192.168.1.1", Port: 8888)
}
func applicationWillTerminate(aNotification: NSNotification?) {
// Insert code here to tear down your application
}
#IBAction func openSettings(sender: AnyObject) {
// open settings for ip and port optional port
}
}
in swift 3:
var myWindow: NSWindow? = nil
let storyboard = NSStoryboard(name: "Main",bundle: nil)
let controller: EditorViewController = storyboard.instantiateController(withIdentifier: "editorViewController") as! ViewController
myWindow = NSWindow(contentViewController: controller)
myWindow?.makeKeyAndOrderFront(self)
let vc = NSWindowController(window: myWindow)
vc.showWindow(self)
For 2022
in your normal Main storyboard, tap to add a new window controller.
tap precisely on the red "X", then the blue circle, and then enter "ExampleID" at the green entry.
in your app's ordinary main view controller, add this
variable:
var otherWindow: NSWindowController?
function:
private func otherWindow() {
let sb = NSStoryboard(name: "Main", bundle: nil)
otherWindow = sb.instantiateController(
withIdentifier: "ExampleID") as! NSWindowController
otherWindow?.showWindow(self)
}
That's it.
Call otherWindow when you want to.
Problem:
Inevitably you will want to set up the otherWindow in a certain way, example, transparent, whatever. Unfortunately this is a whole topic in itself, but you do it like this:
private func otherWindow() {
... as above ...
otherWindow?.window?.ExampleSetup()
}
and then
extension NSWindow {
func ExampleSetup() {
self.styleMask = .borderless
self.collectionBehavior = [.fullScreenPrimary]
self.level = .floating
self.isMovable = false
self.titleVisibility = .hidden
// etc etc etc ..
guard let screen = self.screen ?? NSScreen.main else {
print("what the???")
return
}
self.setFrame(screen.frame, display: true)
// consider also .visibleFrame
}
}
enum Storyboards: String {
case main = "Main"
func instantiateVC<T>(_ identifier: T.Type) -> T? {
let storyboard = NSStoryboard(name: rawValue, bundle: nil)
guard let viewcontroller = storyboard.instantiateController(withIdentifier: String(describing: identifier)) as? T else { return nil}
return viewcontroller
}
}
var ssoLoginController: IDSSOLoginViewController?
var myWindow: NSWindow? = nil
ssoLoginController = Storyboards.main.instantiateVC(IDSSOLoginViewController.self)
myWindow = NSWindow(contentViewController: ssoLoginController!)
myWindow?.makeKeyAndOrderFront(self)
let vc = NSWindowController(window: myWindow)
vc.showWindow(self)
I am not 100% that I fully understand your problem, but assuming that you are using a storyboard (you should if you are starting fresh), adding few lines to your applicationDidFinishLaunching method will help:
var myWindow: NSWindow? = nil
let storyboard = NSStoryboard(name: "Main",bundle: nil)
let controller: SettingsViewController = storyboard?.instantiateControllerWithIdentifier("SettingsViewController") as SettingsViewController
myWindow = controller.window
myWindow?.makeKeyAndOrderFront(self)
Do not forget to set the Storyboard ID in IB (in the example above to SettingsViewController)!

Updates Labels in Swift when using popoverController

I have a question about swift,
I made a popover controller in a UIViewController,which display a list of books
and when the user click on one of the books, the label on the viewController should be updated with the name of the selected book.
But in my case when I select a name of a book, the label does not update
here is the code :
// View Controller
import UIKit
class ViewController: UIViewController,UIPopoverControllerDelegate {
var popoverController : UIPopoverController? = nil
#IBOutlet var bookName : UILabel
var BookNameString : String?{
didSet{
configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let detail = self.BookNameString {
if let label = bookName {
label.text = detail
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func Click(sender : UIButton) {
var tableView:TableViewController = TableViewController(style: UITableViewStyle.Plain)
var popoverContent:UINavigationController = UINavigationController(rootViewController: tableView)
self.popoverController = UIPopoverController(contentViewController: popoverContent)
self.popoverController!.delegate = self
self.popoverController!.presentPopoverFromRect(sender.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
and here is the code of the TableViewController when a row is selected:
// TableViewController
override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!){
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var details = storyboard.instantiateViewControllerWithIdentifier("ViewController") as ViewController
var keyString = bookSectionKeys[indexPath.section]
var bookValues = book.booksLetters[keyString]!
var selectedBookString = bookValues[indexPath.row]
var selectedBookValues = book.bookDictionary[selectedBookString]!
details.BookNameString = selectedBookString
}
I was able to solve this problem a few weeks ago and I want to share the solution with you :)
I solve it using protocols.
OK Here what I did in details:
First I created a protocol in the view that I want it to display inside the popover and I named the protocol "DismissPopoverDelegate"
protocol DismissPopoverDelegate{
func didSelectBook(SelectedBook:String)
}
Then I declared a variable of type DismissPopoverDelegate and name it "delegate"
var delegate: DismissPopoverDelegate?
Then in the didSelectRowAtIndexMethod:
override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!){
var keyString = bookSectionKeys[indexPath.section]
var bookValues = book.booksLetters[keyString]!
var selectedBookString = bookValues[indexPath.row]
delegate?.didSelectBook(selectedBookString)
}
After that inside the View that contains the popover (View Controller), I set the delegate of the view:
class ViewController: UIViewController, DismissPopOverDelegate, UIPopoverControllerDelegate{
Then to make the view confirm to the protocol DismissPopOverDelegate, I implement the method "didSelectBook" Inside the view:
func didSelectBook(SelectedBook:String){
popoverController!.dismissPopoverAnimated(true) // This is Optional
bookName.text = SelectedBook // Make the label text equal to the selected book From the table
}
Finally, I set the delegate to the tableView to View Controller
tableView.delegate = self
That's it :)
Here is the full code of the viewController that contains the popover
import UIKit
class ViewController: UIViewController, DismissPopOverDelegate, UIPopoverControllerDelegate{
#IBOutlet var booksbarButton : UIBarButtonItem
#IBOutlet var bookName : UILabel
var popoverController : UIPopoverController? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func showTableView(sender : UIBarButtonItem) {
var tableView:TableViewController = TableViewController(style: UITableViewStyle.Plain)
tableView.delegate = self
var popoverContent:UINavigationController = UINavigationController(rootViewController: tableView)
self.popoverController = UIPopoverController(contentViewController: popoverContent)
popoverController!.delegate = self
self.popoverController!.presentPopoverFromBarButtonItem(booksbarButton, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
func didSelectBook(SelectedBook:String){
popoverController!.dismissPopoverAnimated(true)
bookName.text = SelectedBook
}
}

Resources