me icon indicating copy to clipboard operation
me copied to clipboard

学习MacOS App (Part 3: 从XIB开始理解单一窗口应用)

Open nonocast opened this issue 3 years ago • 0 comments

新建一个interface为XIB的Swift App,此时Xcode给出的template中关键的代码仅2个文件:

  • AppDelegate.swift
import Cocoa

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet var window: NSWindow!

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

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }
}
  • MainMenu.xib
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17150" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17150"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
            <connections>
                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
            </connections>
        </customObject>
        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
        <customObject id="-3" userLabel="Application"/>
        <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider="target">
            <connections>
                <outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
            </connections>
        </customObject>
        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
        ...
        </menu>
        <window title="hello" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
            <rect key="contentRect" x="335" y="390" width="480" height="360"/>
            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
            <view key="contentView" id="EiT-Mj-1SZ">
                <rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
                <autoresizingMask key="autoresizingMask"/>
            </view>
            <point key="canvasLocation" x="200" y="400"/>
        </window>
    </objects>
</document>

New ViewController

之前在Part 2中因为是通过storyboard方式进行创建,所以看上去一切顺风顺水,但现在的问题是没有ViewController,那么View-Behind的代码写到哪去呢,总不能写到AppDelegate吧。所以需要将MainMenu.xib理解为程序启动的入口,简单来说,你认为他为你创建了一个Window,后面的View和ViewController你自己往上堆。

所以此时应该新建一个ViewController,具体操作就是新建一个Cocoa Class,name: ViewController, Subclass of: NSViewController, 同时勾选创建XIB,改一下layer颜色,增加一个label,然后在AppDelegate中如下:

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet var window: NSWindow!
    var viewController: ViewController!
        
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        viewController = ViewController()
        window.contentView?.addSubview(viewController.view)
        viewController.view.frame = window.contentView!.bounds
        viewController.view.autoresizingMask =  [.width, .height]
    }
}

也可以在XIB中通过Object Library加入一个View Controller,然后通过connect引入到AppDelegate,省去了一个new的过程,其实意思是一致的,

IBOutlet weak var viewController: ViewController!

如果需要隔离AppDelegate和AppController, 则需要创建AppController,然后根据L36的视频将AppController加入到XIB中,即实例化,

AppController.swift

class AppController: NSObject {
   
    @IBOutlet weak var container: NSView!
    @IBOutlet weak var viewController: ViewController!
    
    override func awakeFromNib() {
        container.addSubview(viewController.view)
        viewController.view.frame = container.bounds
        viewController.view.autoresizingMask =  [.width, .height]
    }
}

这样就分离了AppDelegate和AppController,为下一步切换多个View做好了准备。

多个View/ViewController切换

具体参加视频,逻辑来说已经很清晰了

  • 建立AppController
  • 新建FirstViewController和SecondViewController
  • awake时首先添加FirstViewController
  • 在界面上增加两个Button,每次点击通过currentView.removeFromSuperView上移除,然后添加对应的ViewController.view即可

小结:

  1. 很灵活,对Window/WindowController, View/ViewController有了一定的认识
  2. 从学习的角度还是应该理解一下XIB,实践中应该直接用Storyboard,省去了这一堆麻烦事情

参考阅读:

nonocast avatar Jul 17 '21 17:07 nonocast