Drag views in NSStackView to rearrange the sequence - macos

Is it possible to change the order of views in NSStackView by dragging the subviews, just like we do it in NSTableView ?

Here's an implementation of an NSStackView subclass whose contents can be reordered via dragging:
//
// DraggingStackView.swift
// Analysis
//
// Created by Mark Onyschuk on 2017-02-02.
// Copyright © 2017 Mark Onyschuk. All rights reserved.
//
import Cocoa
class DraggingStackView: NSStackView {
var isEnabled = true
// MARK: -
// MARK: Update Function
var update: (NSStackView, Array<NSView>)->Void = { stack, views in
stack.views.forEach {
stack.removeView($0)
}
views.forEach {
stack.addView($0, in: .leading)
switch stack.orientation {
case .horizontal:
$0.topAnchor.constraint(equalTo: stack.topAnchor).isActive = true
$0.bottomAnchor.constraint(equalTo: stack.bottomAnchor).isActive = true
case .vertical:
$0.leadingAnchor.constraint(equalTo: stack.leadingAnchor).isActive = true
$0.trailingAnchor.constraint(equalTo: stack.trailingAnchor).isActive = true
}
}
}
// MARK: -
// MARK: Event Handling
override func mouseDragged(with event: NSEvent) {
if isEnabled {
let location = convert(event.locationInWindow, from: nil)
if let dragged = views.first(where: { $0.hitTest(location) != nil }) {
reorder(view: dragged, event: event)
}
} else {
super.mouseDragged(with: event)
}
}
private func reorder(view: NSView, event: NSEvent) {
guard let layer = self.layer else { return }
guard let cached = try? self.cacheViews() else { return }
let container = CALayer()
container.frame = layer.bounds
container.zPosition = 1
container.backgroundColor = NSColor.underPageBackgroundColor.cgColor
cached
.filter { $0.view !== view }
.forEach { container.addSublayer($0) }
layer.addSublayer(container)
defer { container.removeFromSuperlayer() }
let dragged = cached.first(where: { $0.view === view })!
dragged.zPosition = 2
layer.addSublayer(dragged)
defer { dragged.removeFromSuperlayer() }
let d0 = view.frame.origin
let p0 = convert(event.locationInWindow, from: nil)
window!.trackEvents(matching: [.leftMouseDragged, .leftMouseUp], timeout: 1e6, mode: .eventTrackingRunLoopMode) { event, stop in
if event.type == .leftMouseDragged {
let p1 = self.convert(event.locationInWindow, from: nil)
let dx = (self.orientation == .horizontal) ? p1.x - p0.x : 0
let dy = (self.orientation == .vertical) ? p1.y - p0.y : 0
CATransaction.begin()
CATransaction.setDisableActions(true)
dragged.frame.origin.x = d0.x + dx
dragged.frame.origin.y = d0.y + dy
CATransaction.commit()
let reordered = self.views.map {
(view: $0,
position: $0 !== view
? NSPoint(x: $0.frame.midX, y: $0.frame.midY)
: NSPoint(x: dragged.frame.midX, y: dragged.frame.midY))
}
.sorted {
switch self.orientation {
case .vertical: return $0.position.y < $1.position.y
case .horizontal: return $0.position.x < $1.position.x
}
}
.map { $0.view }
let nextIndex = reordered.index(of: view)!
let prevIndex = self.views.index(of: view)!
if nextIndex != prevIndex {
self.update(self, reordered)
self.layoutSubtreeIfNeeded()
CATransaction.begin()
CATransaction.setAnimationDuration(0.15)
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut))
for layer in cached {
layer.position = NSPoint(x: layer.view.frame.midX, y: layer.view.frame.midY)
}
CATransaction.commit()
}
} else {
view.mouseUp(with: event)
stop.pointee = true
}
}
}
// MARK: -
// MARK: View Caching
private class CachedViewLayer: CALayer {
let view: NSView!
enum CacheError: Error {
case bitmapCreationFailed
}
override init(layer: Any) {
self.view = (layer as! CachedViewLayer).view
super.init(layer: layer)
}
init(view: NSView) throws {
self.view = view
super.init()
guard let bitmap = view.bitmapImageRepForCachingDisplay(in: view.bounds) else { throw CacheError.bitmapCreationFailed }
view.cacheDisplay(in: view.bounds, to: bitmap)
frame = view.frame
contents = bitmap.cgImage
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
private func cacheViews() throws -> [CachedViewLayer] {
return try views.map { try cacheView(view: $0) }
}
private func cacheView(view: NSView) throws -> CachedViewLayer {
return try CachedViewLayer(view: view)
}
}
The code requires your stack to be layer backed, and uses sublayers to simulate and animate its content views during drag handling. Dragging is detected by an override of mouseDragged(with:) so will not be initiated if the stack's contents consume this event.

There's no built-in support for re-ordering NSStackView subviews.

Related

SwiftUI DragGesture list content Leaks memory

I am trying to implement Uber trip like screen, and I have used the code below
public struct OverlappingPanView<BGView, PanContentView>: View
where BGView: View, PanContentView: View {
public var backgroundView: () -> BGView
public var panContentView: () -> PanContentView
public init(backgroundView: #escaping () -> BGView, panContentView: #escaping () -> PanContentView) {
self.backgroundView = backgroundView
self.panContentView = panContentView
}
enum OffsetError : Error{
case TopBoundExceded
case BottomBoundExceded
}
//MARK: - State Property Variables
#GestureState private var dragOffset: CGFloat = 0
#State private var lastEndedOffset: CGFloat = 0
#State private var panFrame: CGSize = .zero
//MARK: - Getters
var staticTopPadding: CGFloat{
let screenPadding = UIScreen.main.bounds.height * 0.4
let actualFrame = (panFrame.height - screenPadding)
let extraCorrection = actualFrame * 0.5
return screenPadding + extraCorrection
}
private var currentPaddingValue: CGFloat{
return dragOffset + lastEndedOffset + staticTopPadding
}
private var difference: CGFloat{
return 200
}
//MARK: - Body
public var body: some View {
ZStack {
backgroundView()
.edgesIgnoringSafeArea(.all)
panControllerView()
}
}
func panControllerView() -> some View{
panContentView()
.background(
GeometryReader { geo -> Color in
DispatchQueue.main.async {
self.panFrame = geo.size
}
return Color.clear
}
)
.frame(minHeight: 0, maxHeight: .infinity)
.offset(y: dragOffset + lastEndedOffset + staticTopPadding)
.animation(.interactiveSpring())
.highPriorityGesture(
DragGesture(coordinateSpace: CoordinateSpace.global)
.updating($dragOffset) { (value, state, _) in
if let endValue = try? self.canDrag(value.translation.height){
state = endValue
}
}
.onEnded { value in
self.onDragEnd(value.translation.height)
}
)
}
func onDragEnd(_ value: CGFloat){
withAnimation {
do{
let endValue = try self.canDrag(value)
self.lastEndedOffset = lastEndedOffset + endValue
}catch OffsetError.BottomBoundExceded{
self.lastEndedOffset = 0
}catch OffsetError.TopBoundExceded{
self.lastEndedOffset = -(self.panFrame.height - difference)
}catch{
self.lastEndedOffset = 0
}
}
}
func canDrag(_ value: CGFloat) throws -> CGFloat{
let newValue = value + lastEndedOffset + staticTopPadding
// stop going below
guard newValue <= staticTopPadding else{
throw OffsetError.BottomBoundExceded
}
let topCalculation = (self.panFrame.height + (value + self.lastEndedOffset)) - difference
// stop going top
guard topCalculation >= 0 else{
throw OffsetError.TopBoundExceded
}
return value
}
}
and Used the above code to show mapview in the background and ForEach content on the front.
OverlappingPanView {
GoogleMapView(delegate: presenter)
} panContentView: {
contentView() // ForEach looped data Views
}
The list that I have provided in content View is leaking memory and uses high CPU (above 100%) when dragging leading to UI Performance issues.

XCode Page View Controller: make dots transparent

i have added a page view controller and a 3 pages.
I set Transition Style to Scroll and implemented the presentationCount and the presentationIndex method. now i get a black bar with gray/white dots at the bottom of my view. However, but i want the view to goright to the bottom and the make the dots appear over it(without the black background.
How do i do that?
here is my code:
import UIKit
class FilterViewController: UIPageViewController, UIPageViewControllerDataSource {
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newViewController(category: "first"),
self.newViewController(category: "second"),
self.newViewController(category: "third")]
}()
private func newViewController(category: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: "\(category)ViewController")
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return orderedViewControllers.last
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return orderedViewControllers.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
let firstViewControllerIndex = orderedViewControllers.index(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}
ok, i got it myself. If anyone has the same problem, just override the viewDidLayoutSubviews() of the Page View Controller:
override func viewDidLayoutSubviews() {
let v = self.view
let subviews = v?.subviews
if subviews?.count == 2 {
var sv:UIScrollView?
var pc:UIPageControl?
for t in subviews! {
if t is UIScrollView {
sv = t as! UIScrollView
} else {
pc = t as! UIPageControl
}
}
if(sv != nil && pc != nil) {
sv?.frame = (v?.bounds)!
v?.bringSubview(toFront: pc!)
}
}
super.viewDidLayoutSubviews()
}

Not able to Rotate my 3D objects

I had created one project using ARKit and SceneKit framework. In which I am working with file extension .dae, the files are locally available in my project as shown in below screenshot.
Here I had applied many gestures on this virtual object such as Tap Gesture(When I tap on camera screen, it places the virtual object there), same way Pinch Gesture and Pan Gesture. All of these gestures are working perfectly fine. Now I wanted to apply rotation gesture, for which I got stuck how to do that, also I am not getting any such available sources to achieve this.
Below is my working code so far,
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
private var movedObject: SCNNode?
private var hud :MBProgressHUD!
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView.autoenablesDefaultLighting = true
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene()
sceneView.scene = scene
registerGestureRecognizers()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
private func registerGestureRecognizers() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped(recognizer:)))
tapGestureRecognizer.numberOfTapsRequired = 1
self.sceneView.addGestureRecognizer(tapGestureRecognizer)
let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(pinched(recognizer:)))
self.sceneView.addGestureRecognizer(pinchGestureRecognizer)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(moveObject(recognizer:)))
panGestureRecognizer.maximumNumberOfTouches = 1
panGestureRecognizer.minimumNumberOfTouches = 1
self.sceneView.addGestureRecognizer(panGestureRecognizer)
let rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(rotateObject(recognizer:)))
self.sceneView.addGestureRecognizer(rotationGestureRecognizer)
}
#objc func pinched(recognizer :UIPinchGestureRecognizer) {
if recognizer.state == .changed {
guard let sceneView = recognizer.view as? ARSCNView else {
return
}
let touch = recognizer.location(in: sceneView)
let hitTestResults = self.sceneView.hitTest(touch, options: nil)
if let hitTest = hitTestResults.first {
let chairNode = hitTest.node
let pinchScaleX = Float(recognizer.scale) * chairNode.scale.x
let pinchScaleY = Float(recognizer.scale) * chairNode.scale.y
let pinchScaleZ = Float(recognizer.scale) * chairNode.scale.z
chairNode.scale = SCNVector3(pinchScaleX,pinchScaleY,pinchScaleZ)
recognizer.scale = 1
}
}
}
#objc func moveObject(recognizer: UIPanGestureRecognizer) {
print("Move object")
if recognizer.state == .began {
print("Pan state began")
let tapPoint: CGPoint? = recognizer.location(in: sceneView)
let result = sceneView.hitTest(tapPoint ?? CGPoint.zero, options: nil)
if result.count == 0 {
return
}
let hitResult: SCNHitTestResult? = result.first
if (hitResult?.node.name == "free_car_1") {
movedObject = hitResult?.node
} else if (hitResult?.node.parent?.name == "free_car_1") {
movedObject = hitResult?.node.parent
}
if (movedObject != nil) {
print("Holding an Object")
}
}
if recognizer.state == .changed {
print("Pan State Changed")
if (movedObject != nil) {
let tapPoint: CGPoint? = recognizer.location(in: sceneView)
let hitResults = sceneView.hitTest(tapPoint ?? CGPoint.zero, types: .featurePoint)
let result: ARHitTestResult? = hitResults.last
let matrix: SCNMatrix4 = SCNMatrix4((result?.worldTransform)!)
//SCNMatrix4FromMat4((result?.worldTransform)!)
let vector: SCNVector3 = SCNVector3Make(matrix.m41, matrix.m42, matrix.m43)
movedObject?.position = vector
print("Moving object position")
}
}
if recognizer.state == .ended {
print("Done moving object homeie")
movedObject = nil
}
}
#objc func tapped(recognizer :UITapGestureRecognizer) {
guard let sceneView = recognizer.view as? ARSCNView else {
return
}
let touch = recognizer.location(in: sceneView)
let hitTestResults = sceneView.hitTest(touch)
guard let hitTest = hitTestResults.first?.node else {
let hitTestResultsWithExistingPlane = sceneView.hitTest(touch, types: .existingPlane)
let chairScene = SCNScene(named: "ShelbyWD.dae")!
guard let chairNode = chairScene.rootNode.childNode(withName: "ShelbyWD", recursively: true) else {
return
}
if let hitTestAvailable = hitTestResultsWithExistingPlane.first {
chairNode.position = SCNVector3(hitTestAvailable.worldTransform.columns.3.x,hitTestAvailable.worldTransform.columns.3.y,hitTestAvailable.worldTransform.columns.3.z)
self.sceneView.scene.rootNode.addChildNode(chairNode)
return
}
return
}
hitTest.removeFromParentNode()
}
#objc func rotateObject(recognizer :UIRotationGestureRecognizer)
{
}
}
Can anyone help me out to apply rotation gesture on my object?
Thank you!
In order to rotate an SCNNode, the 1st thing you need to do, is create a variable to store the rotationAngle around the YAxis or any other that you wish to perform the rotation on e.g:
var currentAngleY: Float = 0.0
Then have some way to have detected to node you wish to rotate, which in my example I am calling currentNode e.g.
var currentNode: SCNNode!
In my example I will just rotate around the YAxis.
You can use a UIPanGestureRecognizer like so:
/// Rotates An Object On It's YAxis
///
/// - Parameter gesture: UIPanGestureRecognizer
#objc func rotateObject(_ gesture: UIPanGestureRecognizer) {
guard let nodeToRotate = currentNode else { return }
let translation = gesture.translation(in: gesture.view!)
var newAngleY = (Float)(translation.x)*(Float)(Double.pi)/180.0
newAngleY += currentAngleY
nodeToRotate.eulerAngles.y = newAngleY
if(gesture.state == .ended) { currentAngleY = newAngleY }
print(nodeToRotate.eulerAngles)
}
Or if you wish to use a UIRotationGesture you can do something like this:
/// Rotates An SCNNode Around It's YAxis
///
/// - Parameter gesture: UIRotationGestureRecognizer
#objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
//1. Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
if gesture.state == .changed{
isRotating = true
currentNode.eulerAngles.y = currentAngleY + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleY = currentNode.eulerAngles.y
isRotating = false
}
}
Hope it helps...

Unrecognized selector sent to instance when using UIPanGesture

Hi I am trying to add UIPanGestureRecognizer to UIImageView (in my case, it's an emoji). All other UIGestureRecognizers such as long press, rotation, and pinch work well. However, it gives me an error: unrecognized selector sent to instance when I add UIPanGestureRecognizer. I've spent a day trying to figure out the reason but failed to fix it. Please help! Thanks in advance.
This is a function where I added UIGestureRecognizer to sticker
func emojiInsert(imageName: String) {
deleteButtonHides()
let stickerView: UIImageView = UIImageView(frame: CGRectMake(backgroundImage.frame.width/2 - 50, backgroundImage.frame.height/2 - 50, stickerSize, stickerSize))
stickerView.image = UIImage(named: imageName)
stickerView.userInteractionEnabled = true
stickerView.accessibilityIdentifier = "sticker"
let deleteStickerButton: UIImageView = UIImageView(frame: CGRectMake(stickerView.frame.width - 5 - stickerView.frame.width/3, 5, stickerView.frame.width/3, stickerView.frame.height/3))
deleteStickerButton.image = UIImage(named: "button_back")
deleteStickerButton.accessibilityIdentifier = "delete"
deleteStickerButton.userInteractionEnabled = true
deleteStickerButton.alpha = 0
deleteStickerButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "deleteButtonTouches:"))
stickerView.addSubview(deleteStickerButton)
stickerView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: "handlePinch:"))
stickerView.addGestureRecognizer(UIRotationGestureRecognizer(target: self, action: "handleRotate:"))
stickerView.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: "handleLongPress:"))
stickerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan"))
print("emojiInsert : \(imageName)")
backgroundImage.addSubview(stickerView)
}
Below are call back functions I added in the end of the view.swift. I used touchesbegan and touchesMoved to drag an emoji but emoji moved in weird way after rotation. So now I am trying to use UIPanGesture to drag an emoji.
#IBAction func handlePinch(recognizer : UIPinchGestureRecognizer) {
if(deleteMode) {
return
}
print("handlePinch \(recognizer.scale)")
if let view = recognizer.view {
view.transform = CGAffineTransformScale(view.transform,
recognizer.scale, recognizer.scale)
recognizer.scale = 1
}
}
#IBAction func handleRotate(recognizer : UIRotationGestureRecognizer) {
if(deleteMode) {
return
}
if let view = recognizer.view {
view.transform = CGAffineTransformRotate(view.transform, recognizer.rotation)
recognizer.rotation = 0
}
}
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
if(deleteMode) {
return
}
let translation = recognizer.translationInView(self.view)
if let view = recognizer.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
recognizer.setTranslation(CGPointZero, inView: self.view)
}
#IBAction func handleLongPress(recognizer: UILongPressGestureRecognizer) {
if(recognizer.state == UIGestureRecognizerState.Began) {
if(!deleteMode) {
print("LongPress - Delete Shows")
for (_, stickers) in self.backgroundImage.subviews.enumerate() {
for (_, deleteButtons) in stickers.subviews.enumerate() {
if let delete:UIImageView = deleteButtons as? UIImageView{
if(delete.accessibilityIdentifier == "delete") {
delete.alpha = 1
}
}
}
}
deleteMode = true
} else {
deleteButtonHides()
}
}
}
Again, please help! Thanks in advance.
The problem is that you're missing a colon. In the following line:
stickerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan"))
The handlePan should be handlePan:. That's because the Objective-C signature for your method is:
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
The colon is part of the method name.

Cocoa: react to keyDown in QLPreviewPanel

I implemented quick look in my project in the following way in Swift 2 (I'm including this here for reference and because it might help someone else set it up).
My NSViewController contains a NSTableView subclass where I implemented keyDown to listen to the spacebar key being pressed (maybe not the best way but it works):
override func keyDown(theEvent: NSEvent) {
let s = theEvent.charactersIgnoringModifiers!
let s1 = s.unicodeScalars
let s2 = s1[s1.startIndex].value
let s3 = Int(s2)
if s3 == Int(" ".utf16.first!) {
NSNotificationCenter.defaultCenter().postNotification(NSNotification(name: "MyTableViewSpacebar", object: nil))
return
}
super.keyDown(theEvent)
}
In my view controller, I have an observer for this notification and the functions required by the QLPreviewPanel:
//...
class ViewController: NSViewController {
#IBOutlet weak var myTableView: MyTableView!
var files = [FilesListData]() //array of custom class
//...
override func viewDidLoad() {
//...
NSNotificationCenter.defaultCenter().addObserver(self, selector: "spaceBarKeyDown:", name: "MyTableViewSpacebar", object: nil)
}
func spaceBarKeyDown(notification: NSNotification) {
if let panel = QLPreviewPanel.sharedPreviewPanel() {
panel.makeKeyAndOrderFront(self)
}
}
override func acceptsPreviewPanelControl(panel: QLPreviewPanel!) -> Bool {
return true
}
override func beginPreviewPanelControl(panel: QLPreviewPanel!) {
panel.delegate = self
panel.dataSource = self
}
override func endPreviewPanelControl(panel: QLPreviewPanel!) {
}
}
extension ViewController: QLPreviewPanelDataSource {
func numberOfPreviewItemsInPreviewPanel(panel: QLPreviewPanel!) -> Int {
return self.myTableView.selectedRowIndexes.count
}
func previewPanel(panel: QLPreviewPanel!, previewItemAtIndex index: Int) -> QLPreviewItem! {
if self.myTableView.selectedRow != -1 {
var items = [QLPreviewItem]()
let manager = NSFileManager.defaultManager()
for i in self.myTableView.selectedRowIndexes {
let path = self.files[i].path //path to a MP3 file
if manager.fileExistsAtPath(path) {
items.append(NSURL(fileURLWithPath: path))
} else {
items.append(qm_url) //image of a question mark used as placeholder
}
}
return items[index]
} else {
return qm_url //image of a question mark used as placeholder
}
}
}
What I would like to do now is listen to the keys "up arrow" and "down arrow" being pressed while the quick look panel is open, in order to change the selected row in the NSTableView, much like Finder behaves when you preview files with quick look. I have no clue as to how I could implement this. Any ideas?
Thanks.
Finally found what I was looking for and it's actually pretty simple.
Since my main view controller is also my delegate for the QLPreviewPanel, I added this:
extension ViewController: QLPreviewPanelDelegate {
func previewPanel(panel: QLPreviewPanel!, handleEvent event: NSEvent!) -> Bool {
let kc = event.keyCode
if (kc == 126 || kc == 125) { //up and down arrows
if event.type == NSEventType.KeyDown {
self.myTableView.keyDown(event) //send the event to the table
} else if event.type == NSEventType.KeyUp {
self.myTableView.keyUp(event)
}
return true
}
return false
}
}
Then in my table view delegate:
func tableViewSelectionDidChange(notification: NSNotification) {
guard myTableView.numberOfSelectedRows > 0 else {
if let panel = QLPreviewPanel.sharedPreviewPanel() {
if panel.visible {
panel.close()
}
}
return
}
if let panel = QLPreviewPanel.sharedPreviewPanel() {
if panel.visible {
panel.reloadData()
}
}
}
That's it! The QLPreviewPanelDataSource handles the rest.

Resources