6

s の独自のタブを描画したいNSTabViewItem。マイ タブの外観は異なり、中央ではなく左上隅から開始する必要があります。

これどうやってするの?

4

6 に答える 6

5

タブを描画する方法の 1 つは、NSCollectionView を使用することです。Swift 4 の例を次に示します。

クラスには、スタイルとカスタムで事前構成されたものTabViewStackControllerが含まれています。TabViewController.unspecifiedTabBarView

class TabViewStackController: ViewController {

   private lazy var tabBarView = TabBarView().autolayoutView()
   private lazy var containerView = View().autolayoutView()
   private lazy var tabViewController = TabViewController()
   private let tabs: [String] = (0 ..< 14).map { "TabItem # \($0)" }

   override func setupUI() {
      view.addSubviews(tabBarView, containerView)
      embedChildViewController(tabViewController, container: containerView)
   }

   override func setupLayout() {
      LayoutConstraint.withFormat("|-[*]-|", forEveryViewIn: containerView, tabBarView).activate()
      LayoutConstraint.withFormat("V:|-[*]-[*]-|", tabBarView, containerView).activate()
   }

   override func setupHandlers() {
      tabBarView.eventHandler = { [weak self] in
         switch $0 {
         case .select(let item):
            self?.tabViewController.process(item: item)
         }
      }
   }

   override func setupDefaults() {
      tabBarView.tabs = tabs
      if let item = tabs.first {
         tabBarView.select(item: item)
         tabViewController.process(item: item)
      }
   }
}

タブを表すクラスTabBarViewが含まれます。CollectionView

class TabBarView: View {

   public enum Event {
      case select(String)
   }

   public var eventHandler: ((Event) -> Void)?

   private let cellID = NSUserInterfaceItemIdentifier(rawValue: "cid.tabView")
   public var tabs: [String] = [] {
      didSet {
         collectionView.reloadData()
      }
   }

   private lazy var collectionView = TabBarCollectionView()
   private let tabBarHeight: CGFloat = 28
   private (set) lazy var scrollView = TabBarScrollView(collectionView: collectionView).autolayoutView()

   override var intrinsicContentSize: NSSize {
      let size = CGSize(width: NSView.noIntrinsicMetric, height: tabBarHeight)
      return size
   }

   override func setupHandlers() {
      collectionView.delegate = self
   }

   override func setupDataSource() {
      collectionView.dataSource = self
      collectionView.register(TabBarTabViewItem.self, forItemWithIdentifier: cellID)
   }

   override func setupUI() {
      addSubviews(scrollView)

      wantsLayer = true

      let gridLayout = NSCollectionViewGridLayout()
      gridLayout.maximumNumberOfRows = 1
      gridLayout.minimumItemSize = CGSize(width: 115, height: tabBarHeight)
      gridLayout.maximumItemSize = gridLayout.minimumItemSize
      collectionView.collectionViewLayout = gridLayout
   }

   override func setupLayout() {
      LayoutConstraint.withFormat("|[*]|", scrollView).activate()
      LayoutConstraint.withFormat("V:|[*]|", scrollView).activate()
   }
}

extension TabBarView {

   func select(item: String) {
      if let index = tabs.index(of: item) {
         let ip = IndexPath(item: index, section: 0)
         if collectionView.item(at: ip) != nil {
            collectionView.selectItems(at: [ip], scrollPosition: [])
         }
      }
   }
}

extension TabBarView: NSCollectionViewDataSource {

   func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
      return tabs.count
   }

   func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
      let tabItem = tabs[indexPath.item]
      let cell = collectionView.makeItem(withIdentifier: cellID, for: indexPath)
      if let cell = cell as? TabBarTabViewItem {
         cell.configure(title: tabItem)
      }
      return cell
   }
}

extension TabBarView: NSCollectionViewDelegate {

   func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
      if let first = indexPaths.first {
         let item = tabs[first.item]
         eventHandler?(.select(item))
      }
   }
}

TabViewControllerスタイルで事前構成されたクラス.unspecified

class TabViewController: GenericTabViewController<String> {

   override func viewDidLoad() {
      super.viewDidLoad()
      transitionOptions = []
      tabStyle = .unspecified
   }

   func process(item: String) {
      if index(of: item) != nil {
         select(itemIdentifier: item)
      } else {
         let vc = TabContentController(content: item)
         let tabItem = GenericTabViewItem(identifier: item, viewController: vc)
         addTabViewItem(tabItem)
         select(itemIdentifier: item)
      }
   }
}

残りのクラス。

class TabBarCollectionView: CollectionView {

   override func setupUI() {
      isSelectable = true
      allowsMultipleSelection = false
      allowsEmptySelection = false
      backgroundView = View(backgroundColor: .magenta)
      backgroundColors = [.clear]
   }
}

class TabBarScrollView: ScrollView {

   override func setupUI() {
      borderType = .noBorder
      backgroundColor = .clear
      drawsBackground = false

      horizontalScrollElasticity = .none
      verticalScrollElasticity = .none

      automaticallyAdjustsContentInsets = false
      horizontalScroller = InvisibleScroller()
   }
}

// Disabling scroll view indicators.
// See: https://stackoverflow.com/questions/9364953/hide-scrollers-while-leaving-scrolling-itself-enabled-in-nsscrollview
private class InvisibleScroller: Scroller {

   override class var isCompatibleWithOverlayScrollers: Bool {
      return true
   }

   override class func scrollerWidth(for controlSize: NSControl.ControlSize, scrollerStyle: NSScroller.Style) -> CGFloat {
      return CGFloat.leastNormalMagnitude // Dimension of scroller is equal to `FLT_MIN`
   }

   override func setupUI() {
      // Below assignments not really needed, but why not.
      scrollerStyle = .overlay
      alphaValue = 0
   }
}

class TabBarTabViewItem: CollectionViewItem {

   private lazy var titleLabel = Label().autolayoutView()

   override var isSelected: Bool {
      didSet {
         if isSelected {
            titleLabel.font = Font.semibold(size: 10)
            contentView.backgroundColor = .red
         } else {
            titleLabel.font = Font.regular(size: 10.2)
            contentView.backgroundColor = .blue
         }
      }
   }

   override func setupUI() {
      view.addSubviews(titleLabel)
      view.wantsLayer = true
      titleLabel.maximumNumberOfLines = 1
   }

   override func setupDefaults() {
      isSelected = false
   }

   func configure(title: String) {
      titleLabel.text = title
      titleLabel.textColor = .white
      titleLabel.alignment = .center
   }

   override func setupLayout() {
      LayoutConstraint.withFormat("|-[*]-|", titleLabel).activate()
      LayoutConstraint.withFormat("V:|-(>=4)-[*]", titleLabel).activate()
      LayoutConstraint.centerY(titleLabel).activate()
   }
}

class TabContentController: ViewController {

   let content: String
   private lazy var titleLabel = Label().autolayoutView()

   init(content: String) {
      self.content = content
      super.init()
   }

   required init?(coder: NSCoder) {
      fatalError()
   }

   override func setupUI() {
      contentView.addSubview(titleLabel)
      titleLabel.text = content
      contentView.backgroundColor = .green
   }

   override func setupLayout() {
      LayoutConstraint.centerXY(titleLabel).activate()
   }
}

これがどのように見えるかです:

結果

于 2018-06-07T08:00:43.863 に答える
4

NSTabViewのスタイルをTablessに設定してから、NSSegmentedCellをサブクラス化してスタイルと動作をオーバーライドするNSSegmentedControlで制御することができます。これを行う方法のアイデアについては、Xcode 4スタイルのタブをエミュレートするこのプロジェクトをチェックしてください:https ://github.com/aaroncrespo/WILLTabView/ 。

于 2011-12-13T01:48:38.893 に答える
3

NSTabViewは、Cocoaで最もカスタマイズ可能なクラスではありませんが、サブクラス化して独自の描画を行うことは可能です。タブビューアイテムのコレクションを維持する以外にスーパークラスの多くの機能を使用することはなく、描画とイベント処理を正しく機能させるために、NSViewメソッドとNSResponderメソッドをいくつか実装することになります。

最初に無料またはオープンソースのタブバーコントロールの1つを確認するのが最善かもしれません。私は過去にPSMTabBarControlを使用しましたが、独自のタブビューサブクラスを実装するよりもはるかに簡単でした(これが置き換えられました)。

于 2009-06-06T12:10:21.027 に答える
3

私は最近、私が取り組んでいた何かのためにこれを行いました。

テーブルのタブビューを使用して終了し、別のビューで自分でタブを描画しました。タブをウィンドウの下部にあるステータス バーの一部にしたかったのです。

かなり簡単なマウス クリックをサポートする必要があることは明らかですが、キーボードのサポートも機能することを確認する必要があります。これはもう少し注意が必要です。0.5 秒後にキーボード アクセスがない場合は、タイマーを実行してタブを切り替える必要があります ( OS Xのやり方を見てください)。アクセシビリティも考慮する必要がありますが、それが機能していることに気付くかもしれません。コードでまだチェックしていません。

于 2009-06-06T13:04:06.750 に答える
1

私はこれに非常に行き詰まりました-そして背景色でNSTabViewを投稿しました-PSMTabBarControlが古くなったため、https://github.com/dirkx/CustomizableTabView/blob/master/CustomizableTabView/CustomizableTabView.m

于 2011-11-11T07:25:09.307 に答える