จะเพิ่ม Tabs ในแอพที่ไม่ใช่เอกสารภายใต้ macOS ได้อย่างไร

ฉันชอบสร้างแอปใน Swift 3 ด้วย Xcode 8 และควรเปิดใช้งานแถบแท็บ Apple มันไม่ได้เป็นตามเอกสาร ฉันได้เรียนรู้ที่นี่ว่าสามารถเปิดใช้งานแท็บได้หากฉัน แทนที่วิธีการ @IBAction func newWindowForTab(_ sender: Any?) เช่นในตัวควบคุมหน้าต่าง เพื่อทดสอบสิ่งนี้ ฉันได้สร้างโปรเจ็กต์ใหม่ใน Xcode โดยใช้กระดานเรื่องราว เพิ่มคลาสย่อย NSWindowController และกำหนดไว้ในกระดานเรื่องราว ฉันก็นำไปปฏิบัติ

@IBAction override func newWindowForTab(_ sender: Any?) {}

และแถบแท็บจะปรากฏขึ้นเมื่อมีการสร้างแอป เมื่อสร้างใหม่ ฉันสังเกตเห็นว่าปุ่ม "+" จะปรากฏขึ้นก็ต่อเมื่อมองไม่เห็นแถบแท็บเมื่อปิดแอปก่อนที่จะสร้าง นั่นเป็นข้อผิดพลาดหรือไม่? ฉันจะเพิ่มแท็บใหม่ได้อย่างไร


person DaPhil    schedule 23.10.2016    source แหล่งที่มา
comment
ด้วย addTabbedWindow (หน้าต่าง: สั่งซื้อ :)   -  person Peter Ahlberg    schedule 05.11.2016
comment
@ Dis3buted ขอบคุณ แต่ฉันไม่สามารถทำให้มันทำงานอย่างที่ฉันต้องการได้ โดยทั่วไปฉันต้องการพฤติกรรมเช่นเดียวกับใน Safari: มีหน้าต่างเริ่มต้นบางประเภทที่เปิดขึ้นเมื่อแอปเปิดตัวและโดยการคลิกปุ่ม +- หน้าต่างเริ่มต้นอื่นจะถูกเพิ่มในแท็บ ดังนั้นในโปรเจ็กต์ใหม่ (ที่ไม่ใช่แบบเอกสาร) ฉันจึงจัดคลาสย่อย NSWindowController และกำหนดไว้ในสตอรี่บอร์ด ในสตอรี่บอร์ด ฉันมี View Controller Scene อยู่แล้ว ซึ่งฉันต้องการให้ปรากฏในแต่ละหน้าต่างแบบแท็บใหม่ แต่ฉันไม่ทราบวิธีแทนที่ newWindowForTab อย่างถูกต้องใน NSWindowController ของฉันเพื่อให้มุมมองจากกระดานเรื่องราวของฉันปรากฏขึ้น   -  person DaPhil    schedule 06.11.2016


คำตอบ (3)


ตกลง นี่คือไฟล์ใหม่

ผู้แทน

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Insert code here to initialize your application
}

func applicationWillTerminate(_ aNotification: Notification) {
    // Insert code here to tear down your application
}

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
    return true
}

@IBAction func newWindowForTab(_ sender: Any?){
} // without this the + button doesn't show from start

}

ตัวควบคุมมุมมอง

import Cocoa

class ViewController: NSViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
}

override var representedObject: Any? {
    didSet {
    // Update the view, if already loaded.
    }
}
}

และวินโดว์คอนโทรลเลอร์

import Cocoa

class WindowController: NSWindowController {

var subview: WindowController?

override func windowDidLoad() {
    super.windowDidLoad()
    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}

@IBAction override func newWindowForTab(_ sender: Any?) {

    let story = self.storyboard
    let windowVC: WindowController = story?.instantiateInitialController() as! WindowController

    self.window?.addTabbedWindow(windowVC.window!, ordered: .above)
    self.subview = windowVC

    windowVC.window?.orderFront(self.window)
    windowVC.window?.makeKey()
}

}

คุณต้องเพิ่มรายการเมนูและเชื่อมต่อกับ FirstResponder ในมุมมองเมนูไปยัง newWindowForTab: action, กำหนดคีย์, พูด cmd+t ให้ทำงาน ตัวอย่างนี้เพิ่งเพิ่มแท็บจากปุ่ม + และตัวเลือกเมนูหน้าต่างใช้งานได้ "ย้ายแท็บไปที่ใหม่ window" และ "รวมหน้าต่างทั้งหมด" คุณสามารถลากแท็บออกแล้วปล่อยกลับ ย้ายแท็บในแนวนอน ดูเหมือนว่าจะได้ผล

ทำด้วย Xcode เวอร์ชัน 8.2 เบต้า (8C30a)

person Peter Ahlberg    schedule 16.11.2016
comment
เหตุใดคุณจึงจัดเก็บ windowVC ที่โหลดใหม่ลงในตัวแปร subview แค่สงสัย. - person Eimantas; 28.07.2017
comment
เล่นกับมัน ลองโดยไม่ต้องสร้างและทำลายการดูย่อย - person Peter Ahlberg; 28.07.2017
comment
ขอบคุณสำหรับสิ่งนี้! ฉันสงสัยด้วยว่าทำไมคุณถึงเก็บ windowVC ที่โหลดใหม่ไว้ในตัวแปร subview เช่น @Eimantas ที่ถามด้วย ทำงานได้ดีสำหรับฉันหากไม่มีมัน (xcode 9.2) - person JohnV; 27.09.2018
comment
คิดออกแล้ว - หากไม่มีตัวแปร subview คุณจะไม่สามารถสร้างมากกว่าสองแท็บได้ ขอบคุณอีกครั้ง - person JohnV; 27.09.2018
comment
subview ค่อนข้างทำให้แน่ใจว่าตัวควบคุมของหน้าต่างใหม่จะถูกเก็บไว้ ถ้าคุณไม่เก็บวัตถุนั้น วัตถุนั้นจะถูกเก็บขยะ เนื่องจาก windowVC เป็นประเภทเดียวกับตัวจัดการเหตุการณ์ คุณจึงสามารถตั้งค่า windowVC.window?.windowController = self และให้ปุ่ม + และรายการเมนูทั้งหมดยังคงทำงานอยู่ ดูคำตอบของฉันด้านล่าง - person ctietze; 23.01.2019
comment
อย่าลืมตั้งค่าคลาสเป็น WindowController ใน Identity Inspector บนหน้าต่าง - person Philip Borges; 04.08.2021

ตามแนวคิด นี่คือสิ่งที่เกิดขึ้น:

  • การเรียก NSWindow.addTabbedWindow(_:ordered:) เพื่อเพิ่มหน้าต่างให้กับแถบแท็บดั้งเดิมก็เพียงพอแล้ว
  • เมื่อคุณใส่ NSResponder.newWindowForTab(_:) ลงในห่วงโซ่การตอบกลับของหน้าต่างหลัก ปุ่ม "+" จะมองเห็นได้
  • เมื่อคุณตั้งค่า window.tabbingMode = .preferred แถบแท็บจะมองเห็นได้เสมอ

อย่างไรก็ตาม มีข้อควรระวังบางประการเมื่อนำวิธีการเหล่านี้ไปใช้

นำไปใช้ newWindowForTab

แล้วจะเติม @IBAction override func newWindowForTab(_ sender: Any?) ได้ที่ไหนถึงจะโทร NSWindow.addTabbedWindow(_:ordered:) ได้?

  • หากคุณใช้สตอรี่บอร์ด ให้ใส่สิ่งนี้ลงในคลาสย่อย NSWindowController ที่คุณเป็นเจ้าของ นั่นเป็นวิธีที่ง่ายที่สุดในการไปที่ NSWindow เพื่อโทร addTabbedWindow
  • หากคุณใช้ Xibs AppDelegate จะมีการอ้างอิงไปยังหน้าต่างหลัก คุณสามารถใส่วิธีการได้ที่นี่
  • หากคุณใช้การตั้งค่าแบบเป็นโปรแกรม ให้วางไว้ทุกที่ที่คุณรู้จักอินสแตนซ์ NSWindow หลัก

ทำให้ปุ่ม "+" ใช้งานได้

TL;DR: เมื่อคุณเริ่มต้นหน้าต่างใหม่ ให้เก็บ windowController ของหน้าต่างไว้ที่ไหนสักแห่ง คุณต้องรักษาการอ้างอิงที่รัดกุมเพื่อไม่ให้จัดการเหตุการณ์หน้าต่าง (ในคอนโทรลเลอร์)

ฉันเขียนแอปตัวอย่างด้วย TabManager ที่ดูแลสิ่งนี้: https://github.com/DivineDominion/NSWindow-Tabbing

และบล็อกโพสต์พร้อมรายละเอียด: https://christiantietze.de/posts/2019/07/nswindow-tabbing-multiple-nswindowcontroller/

คำนึงถึงวิธีการจัดกิจกรรมต่างๆ ข้อความเมนูหลักจะถูกส่งไปตาม สายการตอบกลับ และ newWindowForTab ก็เช่นกัน NSApp.sendAction จะล้มเหลวสำหรับเหตุการณ์มาตรฐาน หากแหล่งที่มาของการโทรเชื่อมต่อไม่สุด นั่นหมายความว่า อย่างน้อยก็สูงถึง NSWindowController ของคุณ หรืออาจจะถึง AppDelegate ของคุณด้วยซ้ำ

คุณต้องแน่ใจว่าหน้าต่างเพิ่มเติมใดๆ ที่คุณเพิ่มนั้น แท้จริงแล้วเป็นส่วนหนึ่งของห่วงโซ่การตอบกลับเดียวกันกับหน้าต่างเดิม ไม่เช่นนั้นรายการเมนูจะหยุดทำงาน (และเป็นสีเทา/ปิดใช้งาน) ในทำนองเดียวกัน ปุ่ม "+" จะหยุดทำงานเมื่อคุณคลิก

นี่คือสิ่งที่ @JohnV ในความคิดเห็นของคำตอบอื่นที่เรียกว่า: "หากไม่มีตัวแปรมุมมองย่อย คุณไม่สามารถสร้างมากกว่าสองแท็บได้" นั่นคือผลกระทบ แต่ไม่ใช่คำอธิบายที่แท้จริง คุณสามารถสร้างแท็บเพิ่มเติมได้ตลอดเวลา แต่เฉพาะจากหน้าต่าง/แท็บเดิมเท่านั้น ไม่ใช่จากหน้าต่างใหม่ นั่นเป็นเพราะอีกแท็บหนึ่งไม่ตอบสนองต่อ newWindowForTab

"อีกแท็บหนึ่ง" เองนั้นเป็นเพียง NSWindow อย่างไรก็ตาม การใช้งาน newWindowForTab ของคุณอยู่ใน ตัวควบคุม นั่นก็ขึ้นมาหนึ่งระดับแล้ว

การปรับโค้ดโดย @Peter Ahlberg สิ่งนี้จะได้ผล:

class WindowController: NSWindowController {

    @IBAction override func newWindowForTab(_ sender: Any?) {

        let windowController: WindowController = self.storyboard?.instantiateInitialController() as! WindowController
        let newWindow = windowController.window

        self.window?.addTabbedWindow(newWindow, ordered: .above)

        newWindow.orderFront(self.window)
        newWindow.makeKey()

        // Store the windowController in a collection of sorts
        // to keep a strong reference and make it handle events:
        // (NSApp.delegate as? AppDelegate).addManagedWindowController(windowController)
    }
}

ฉันไม่จำเป็นต้องเพิ่ม newWindowForTab ถึง AppDelegate เพื่อให้ทุกอย่างทำงานได้โดยใช้สตอรี่บอร์ด เพราะวิธีนี้ทำให้ตัวควบคุมหน้าต่างทำงานต่อไปและไม่ต้องการทางเลือกอื่น!

person ctietze    schedule 23.01.2019
comment
คำตอบที่ดี แต่การแบ่งปันคอนโทรลเลอร์ทำให้การจัดการร้านเป็นฝันร้าย ฉันต่อสู้กับแนวทางของคุณ จนกระทั่งพบโพสต์ดีๆ ที่อธิบายวิธีใช้ตัวควบคุมแยกกันสำหรับแต่ละแท็บ ปรากฎว่าโพสต์บนบล็อกก็เป็นของคุณเช่นกัน! โปรดอัปเดตคำตอบเพื่อรวมลิงก์ไปยังโพสต์ของคุณอย่างน้อย: christiantietze.de/posts/2019/07/ - person kontiki; 20.09.2019
comment
โอ้โห คุณพูดถูก ขอบคุณ! ได้เรียบเรียงคำตอบใหม่แล้ว - person ctietze; 20.09.2019

ในกรณีที่อยู่เหนือ "+" จะเพิ่มแท็บใหม่หลังหน้าต่างแรกเสมอ หากคุณปิดหน้าต่างแรก แท็บนั้นจะถูกสร้างขึ้นใหม่ภายใต้หน้าต่างปัจจุบัน

มีวิธีทำให้มันใช้งานได้

override func newWindowForTab(_ sender: Any?) {
    let wc: NSWindowController = self.storyboard?.instantiateInitialController() as! NSWindowController
    wc.window?.windowController = self
    NSApplication.shared.mainWindow!.addTabbedWindow(wc.window!, ordered: .above)
    wc.window?.orderFront(self)
}

เคล็ดลับใน NSApplication.shared.mainWindow! คุณจะเพิ่มแท็บในหน้าต่างที่ใช้งานอยู่ในปัจจุบันเสมอ

และหากเราต้องการสร้างหน้าต่างในตอนท้าย เราก็ควรใช้เคล็ดลับนี้

let tabbedWindows = NSApplication.shared.mainWindow!.tabbedWindows!
let lastTabIdx = tabbedWindows.count - 1
tabbedWindows[lastTabIdx].addTabbedWindow(wc.window!, ordered: .above)
person user840250    schedule 09.09.2019