Subclass UIButton: Property not initialized at super.init call - uibutton

I'm trying to subclass a UIButton so I can do custom animations on them but I'm running into a init problem
Property 'self.originalWidth' not initialized at super.init call
Here's my code:
import UIKit
enum ButtonType {
case newGameButton
case showStatsButton
case addTeamButton
case addUserButton
}
class LSS_Button: UIButton {
var myButtonType:ButtonType?
var originalWidth:CGFloat
var originalHeight:CGFloat
required init?(coder aDecoder: NSCoder) {
fatalError("Something went terribly wrong with init on LSS_Button")
}
override init(frame: CGRect) {
super.init(frame: frame)
self.originalWidth = self.frame.size.width
self.originalHeight = self.frame.size.height
}
How can I resolve this? Thanks in advance.
Forgot to add, if I put self.originalWidth = ... above the super.init then I get this error:
'self' used before super.init call

Dang it, figured out like 5 seconds later:
override init(frame: CGRect) {
self.originalWidth = frame.size.width
self.originalHeight = frame.size.height
super.init(frame: frame)
}

Related

Focus for UICollectionViewCells on tvOS

I'm new to tvOS. I have created UICollectionView using a XIB file in Objective-C. How can I focus UICollectionViewCell?
I have a label and an image view in the cell, and I want to focus both the label and the image view.
UICollectionViewCell are not focus appearance by default, but you can achive this by adding one yourself.
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
if (self.focused)
{
// Apply focused appearence,
// e.g scale both of them using transform or apply background color
}
else
{
// Apply normal appearance
}
}
OR
If you just want to focus ImageView like scaling it up when collection view cell get focus you can do like this in awakeFromNib method
self.imageView.adjustsImageWhenAncestorFocused = YES;
self.clipToBounds = NO;
Add this in custom class of collectionview:
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
if (self.focused) {
collectionView.image.adjustsImageWhenAncestorFocused = true
} else {
collectionView.image.adjustsImageWhenAncestorFocused = false
}
}
Add below methods to your collectionview cell.It will show title of focussed cell only giving complete look and feel of focus.
override func awakeFromNib() {
super.awakeFromNib()
// These properties are also exposed in Interface Builder.
imageView.adjustsImageWhenAncestorFocused = true
imageView.clipsToBounds = false
title.alpha = 0.0
}
// MARK: UICollectionReusableView
override func prepareForReuse() {
super.prepareForReuse()
// Reset the label's alpha value so it's initially hidden.
title.alpha = 0.0
}
// MARK: UIFocusEnvironment
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
/*
Update the label's alpha value using the `UIFocusAnimationCoordinator`.
This will ensure all animations run alongside each other when the focus
changes.
*/
coordinator.addCoordinatedAnimations({
if self.focused {
self.title.alpha = 1.0
}
else {
self.title.alpha = 0.0
}
}, completion: nil)
}

Is there any way to add mouseOver events to a tableView in cocoa?

I want to make my tableView act like this:
When the mouse swipe over a certain row, the row will be highlighted, just like the mouseOver event of a button
It took me some time to work on it based on this hint.
It works for me, correct me if I'm wrong.
Tested with Swift 3.0.2 on macOS 10.12.2 and Xcode 8.2.1
//
// Created by longkai on 30/12/2016.
// Copyright (c) 2016 xiaolongtongxue.com. All rights reserved.
//
import Cocoa
class InboxTableCellView: NSTableCellView {
// MARK: - Outlets
#IBOutlet weak var title: NSTextField!
#IBOutlet weak var sender: NSTextField!
#IBOutlet weak var time: NSTextField!
#IBOutlet weak var snippet: NSTextField!
// MARK: - Mouse hover
deinit {
removeTrackingArea(trackingArea)
}
private var trackingArea: NSTrackingArea!
override func awakeFromNib() {
super.awakeFromNib()
self.trackingArea = NSTrackingArea(
rect: bounds,
options: [NSTrackingAreaOptions.activeAlways, NSTrackingAreaOptions.mouseEnteredAndExited,/* NSTrackingAreaOptions.mouseMoved */],
owner: self,
userInfo: nil
)
addTrackingArea(trackingArea)
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
NSColor(red: 0.96, green: 0.96, blue: 0.96, alpha: 1.00).set()
// mouse hover
if highlight {
let path = NSBezierPath(rect: bounds)
path.fill()
}
// draw divider
let rect = NSRect(x: 0, y: bounds.height - 2, width: bounds.width, height: bounds.height)
let path = NSBezierPath(rect: rect)
path.fill()
}
private var highlight = false {
didSet {
setNeedsDisplay(bounds)
}
}
override func mouseEntered(with event: NSEvent) {
super.mouseEntered(with: event)
if !highlight {
highlight = true
}
}
override func mouseExited(with event: NSEvent) {
super.mouseExited(with: event)
if highlight {
highlight = false
}
}
}
You need to create a subclass and use tracking areas. That's what buttons do to track mouse hovering.
There's an Apple Sample Code that does exactly what you need - highlighting rows on hover:
HoverTableDemo
It's been thoroughly discussed in WWDC 2011 Session 120
(Ignoring the "Mouse Over is bad GUI" sermon (which you'll ignore anyway… ;-))
#import "MoTableView.h"
#implementation MoTableView
{
NSUInteger mouseRow;
NSRect mouseRowFrame;
}
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
mouseRow = -1;
}
return self;
}
- (void)awakeFromNib
{
[self.window setAcceptsMouseMovedEvents:YES];
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
[[NSColor redColor] set];
NSLogDebug(#"mouseRowFrame: %#", NSStringFromRect(mouseRowFrame));
NSFrameRectWithWidth(mouseRowFrame, 2.);
}
- (void)mouseMoved:(NSEvent *)theEvent
{
NSPoint mouseLocation = [theEvent locationInWindow];
NSPoint viewLocation = [self convertPoint:mouseLocation fromView:nil] ;
NSInteger row = [self rowAtPoint:viewLocation];
if (row != mouseRow) {
mouseRowFrame = [self rectOfRow:row];
[self setNeedsDisplay];
mouseRow = row;
}
}
#end

Customize right click highlight on view-based NSTableView

I have a view-based NSTableView with a custom NSTableCellView and a custom NSTableRowView. I customized both of those classes because I want to change the appearance of each row. By implementing the [NSTableRowView draw...] methods I can change the background, the selection, the separator and the drag destination highlight.
My question is: how can I change the highlight that appears when the row is right clicked and a menu appears?
For example, this is the norm:
And I want to change the square highlight to a round one, like this:
I'd imagine this would be done in NSTableRowView by calling a method like drawMenuHighlightInRect: or something, but I can't find it. Also, how can the NSTableRowView class be doing this if I customized, in my subclass, all of the drawing methods, and I don't call the superclass? Is this drawn by the table itself?
EDIT:
After some more experimenting I found out that the round highlight can be achieved by setting the tableview as a source list. Nonetheless, I want to know how to customize it if possible.
I know I'm a bit late to offer any help to the OP, but hopefully this can spare some other folks a little bit of time. I subclassed NSTableRowView to achieve the right-click contextual menu highlight (why Apple doesn't have a public drawing method to override this is beyond me). Here it is in all its glory:
BSDSourceListRowView.h
#import <Cocoa/Cocoa.h>
#interface BSDSourceListRowView : NSTableRowView
// This needs to be set when a context menu is shown.
#property (nonatomic, assign, getter = isShowingMenu) BOOL showingMenu;
#end
BSDSourceListRowView.m
#import "BSDSourceListRowView.h"
#implementation BSDSourceListRowView
- (void)drawBackgroundInRect:(NSRect)dirtyRect
{
[super drawBackgroundInRect:dirtyRect];
// Context menu highlight:
if ( self.isShowingMenu ) {
[self drawContextMenuHighlight];
}
}
- (void)drawContextMenuHighlight
{
BOOL selected = self.isSelected;
CGFloat insetY = ( selected ) ? 2.f : 1.f;
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(self.bounds, 2.f, insetY) xRadius:6.f yRadius:6.f];
NSColor *fillColor, *strokeColor;
if ( selected ) {
fillColor = [NSColor clearColor];
strokeColor = [NSColor whiteColor];
} else {
fillColor = [NSColor colorWithCalibratedRed:95.f/255.f green:159.f/255.f blue:1.f alpha:0.12f];
strokeColor = [NSColor alternateSelectedControlColor];
}
[fillColor setFill];
[strokeColor setStroke];
[path setLineWidth:2.f];
[path fill];
[path stroke];
}
- (void)drawSelectionInRect:(NSRect)dirtyRect
{
[super drawSelectionInRect:dirtyRect];
if ( self.isShowingMenu ) {
[self drawContextMenuHighlight];
}
}
- (void)setShowingMenu:(BOOL)showingMenu
{
if ( showingMenu == _showingMenu )
return;
_showingMenu = showingMenu;
[self setNeedsDisplay:YES];
}
#end
Feel free to use any of it, change any of it, or do whatever you want with any of it. Have fun!
Updated for Swift 3.x:
SourceListRowView.swift
import Cocoa
open class SourceListRowView : NSTableRowView {
open var isShowingMenu: Bool = false {
didSet {
if isShowingMenu != oldValue {
needsDisplay = true
}
}
}
override open func drawBackground(in dirtyRect: NSRect) {
super.drawBackground(in: dirtyRect)
if isShowingMenu {
drawContextMenuHighlight()
}
}
override open func drawSelection(in dirtyRect: NSRect) {
super.drawSelection(in: dirtyRect)
if isShowingMenu {
drawContextMenuHighlight()
}
}
private func drawContextMenuHighlight() {
let insetY: CGFloat = isSelected ? 2 : 1
let path = NSBezierPath(roundedRect: bounds.insetBy(dx: 2, dy: insetY), xRadius: 6, yRadius: 6)
let fillColor, strokeColor: NSColor
if isSelected {
fillColor = .clear
strokeColor = .white
} else {
fillColor = NSColor(calibratedRed: 95/255, green: 159/255, blue: 1, alpha: 0.12)
strokeColor = .alternateSelectedControlColor
}
fillColor.setFill()
strokeColor.setStroke()
path.lineWidth = 2
path.fill()
path.stroke()
}
}
Note: I haven't actually run this, but I'm pretty sure this should do the trick in Swift.
Stop Default Drawing
Several answers describe how to draw a custom contextual-click highlight. However, AppKit will continue to draw the default one. There is an easy trick to stop that and I didn't want it to get lost in a comment: subclass NSTableView and override -menuForEvent:
// NSTableView subclass
override func menu(for event: NSEvent) -> NSMenu?
{
// DO NOT call super's implementation.
return self.menu
}
Here, I assume that you've assigned a menu to the tableView in IB or have set the tableView's menu property programatically. NSTableView's default implementation of -menuForEvent: is what draws the contextual menu highlight.
Solve Bad Apple Engineering
Now that we're not calling super's implementation of menuForEvent:, the clickedRow property of our tableView will always be -1 when we right-click, which means our menuItems won't target the correct row of our tableView.
But fear not, we can do Apple Engineering's job for them. On our custom NSTableView subclass, we override the clickedRow property:
class MyTableView: NSTableView
{
private var _clickedRow: Int = -1
override var clickedRow: Int {
get { return _clickedRow }
set { _clickedRow = newValue }
}
}
Now we update the -menuForEvent: method:
// NSTableView subclass
override func menu(for event: NSEvent) -> NSMenu?
{
let location: CGPoint = convert(event.locationInWindow, from: nil)
clickedRow = row(at: location)
return self.menu
}
Great. We solved that problem. Onwards to the next thing:
Tell Your RowView To Do Custom Drawing
As others have suggested, add a custom Bool property to your NSTableRowView subclass. Then, in your drawing code, inspect that value to decide whether to draw your custom contextual highlight. However, the correct place to set that value is in the same NSTableView method:
// NSTableView subclass
override func menu(for event: NSEvent) -> NSMenu?
{
let location: CGPoint = convert(event.locationInWindow, from: nil)
clickedRow = row(at: location)
if clickedRow > 0,
let rowView: MyCustomRowView = rowView(atRow: tableRow, makeIfNecessary: false) as? MyCustomRowView
{
rowView.isContextualMenuTarget = true
}
return self.menu
}
Above, I've created MyCustomRowView (a subclass of NSTableRowView) and have added a custom property: isContextualMenuTarget. That custom property looks like this:
// NSTableRowView subclass
var isContextualMenuTarget: Bool = false {
didSet {
needsDisplay = true
}
}
In my drawing method, I inspect the value of that property and, if it's true, draw my custom highlight.
Clean Up When The Menu Closes
You have a controller that implements the datasource and delegate methods for your tableView. That controller is also likely the delegate for the tableView's menu. (You can assign that in IB or programatically.)
Whatever object is your menu's delegate, implement the menuDidClose: method. Here, I'm working in Objective-C because my controller is still ObjC:
// NSMenuDelegate object
- (void) menuDidClose:(NSMenu *)menu
{
// We use a custom flag on our rowViews to draw our own contextual menu highlight, so we need to reset that.
[_outlineView enumerateAvailableRowViewsUsingBlock:^(__kindof MyCustomRowView * _Nonnull rowView, NSInteger row) {
rowView.isContextualMenuTarget = NO;
}];
}
Performance Note: My tableView will never have more than about 50 entries. If you have a table with THOUSANDS of visible rows, you would be better served to save the rowView that you set isContextualMenuTarget=true on, then access that rowView directly in -menuDidClose: so you don't have to enumerate all rowViews.
Single-Column: This example assumes a single column tableView that has the same NSMenu for each row. You could adapt the same technique for multi-column and/or varying NSMenus per row.
And that's how you beat AppKit in the face until it does what you want.
This is already a bit old, but I've wasted on it quite a bit of time, so posting my solution in case it could help anyone:
In my case, I wanted to remove the lines completely
Lines are not "Focus" rings, they are some stuff Apple is doing in undocument API
The ONLY way I found to remove them (Without using Undocumented API) is by opening NSMenu programmatically, without Interface Builder.
For that, I had to cache "right-click" event on TableViewRow, which has some issue since not always called, so I've dealt with that issue too.
A. Subclass NSTableView:
Overriding right click event, calculating the location of click to get a correct row, and transferring it to my custom NSTableRowView!
class TableView: NSTableView {
override func rightMouseDown(with event: NSEvent) {
let location = event.locationInWindow
let toMyOrigin = self.superview?.convert(location, from: nil)
let rowIndex = self.row(at: toMyOrigin!)
if (rowIndex < 0 || self.numberOfRows < rowIndex) {
return
}
if let isRowExists = self.rowView(atRow: rowIndex, makeIfNecessary: false) {
if let isMyTypeRow = isRowExists as? MyNSTableRowView {
isMyTypeRow.costumRightMouseDown(with: event)
}
}
}
}
B. Subclass MyNSTableRowView
Presenting NSMenu programmatically
class MyNSTableRowView: NSTableRowView {
//My custom selection colors, don't have to implement this if you are ok with the default system highlighted background color
override func drawSelection(in dirtyRect: NSRect) {
if self.selectionHighlightStyle != .none {
let selectionRect = NSInsetRect(self.bounds, 0, 0)
Colors.tabSelectedBackground.setStroke()
Colors.tabSelectedBackground.setFill()
let selectionPath = NSBezierPath.init(roundedRect: selectionRect, xRadius: 0, yRadius: 0)
selectionPath.fill()
selectionPath.stroke()
}
}
func costumRightMouseDown(with event: NSEvent) {
let menu = NSMenu.init(title: "Actions:")
menu.addItem(NSMenuItem.init(title: "Some", action: #selector(foo), keyEquivalent: "a"))
NSMenu.popUpContextMenu(menu, with: event, for: self)
}
#objc func foo() {
}
}
I agree with MCMatan that this is not something you can tweak by changing any draw calls. The box will remain.
I took his approach of bypassing the default menu launch, but left the context menu setup as default in my NSTableView. I think this is a simpler way.
I derive from NSTableView and add the following:
public private(set) var rightClickedRow: Int = -1
override func rightMouseDown(with event: NSEvent)
{
guard let menu = self.menu else { return }
let windowClickLocation = event.locationInWindow
let outlineClickLocation = convert(windowClickLocation, from: nil)
rightClickedRow = row(at: outlineClickLocation)
menu.popUp(positioning: nil, at: outlineClickLocation, in: self)
}
override func rightMouseUp(with event: NSEvent) {
rightClickedRow = -1
}
My rightClickedRow is analogous to clickedRow for the table view. I have an NSViewController that looks after my table, and it is set as the table's menu delegate. I can use rightClickedRow in the delegate calls, such as menuNeedsUpdate().
I'd take a look at the NSTableRowView documentation. It's the class that is responsible for drawing selection and drag feedback in a view-based NSTableView.

Best way to change the background color for an NSView

I'm looking for the best way to change the backgroundColor of an NSView. I'd also like to be able to set the appropriate alpha mask for the NSView. Something like:
myView.backgroundColor = [NSColor colorWithCalibratedRed:0.227f
green:0.251f
blue:0.337
alpha:0.8];
I notice that NSWindow has this method, and I'm not a big fan of the NSColorWheel, or NSImage background options, but if they are the best, willing to use.
Yeah, your own answer was right. You could also use Cocoa methods:
- (void)drawRect:(NSRect)dirtyRect {
// set any NSColor for filling, say white:
[[NSColor whiteColor] setFill];
NSRectFill(dirtyRect);
[super drawRect:dirtyRect];
}
In Swift:
class MyView: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// #1d161d
NSColor(red: 0x1d/255, green: 0x16/255, blue: 0x1d/255, alpha: 1).setFill()
dirtyRect.fill()
}
}
An easy, efficient solution is to configure the view to use a Core Animation layer as its backing store. Then you can use -[CALayer setBackgroundColor:] to set the background color of the layer.
- (void)awakeFromNib {
self.wantsLayer = YES; // NSView will create a CALayer automatically
}
- (BOOL)wantsUpdateLayer {
return YES; // Tells NSView to call `updateLayer` instead of `drawRect:`
}
- (void)updateLayer {
self.layer.backgroundColor = [NSColor colorWithCalibratedRed:0.227f
green:0.251f
blue:0.337
alpha:0.8].CGColor;
}
That’s it!
If you are a storyboard lover, here is a way that you don't need any line of code.
Add NSBox as a subview to NSView and adjust NSBox's frame as the same with NSView.
In Storyboard or XIB change Title position to None, Box type to Custom, Border Type to "None", and Border color to whatever you like.
Here is a screenshot:
This is the result:
If you setWantsLayer to YES first, you can directly manipulate the layer background.
[self.view setWantsLayer:YES];
[self.view.layer setBackgroundColor:[[NSColor whiteColor] CGColor]];
Think I figured out how to do it:
- (void)drawRect:(NSRect)dirtyRect {
// Fill in background Color
CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
CGContextSetRGBFillColor(context, 0.227,0.251,0.337,0.8);
CGContextFillRect(context, NSRectToCGRect(dirtyRect));
}
edit/update: Xcode 8.3.1 • Swift 3.1
extension NSView {
var backgroundColor: NSColor? {
get {
guard let color = layer?.backgroundColor else { return nil }
return NSColor(cgColor: color)
}
set {
wantsLayer = true
layer?.backgroundColor = newValue?.cgColor
}
}
}
usage:
let myView = NSView(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
print(myView.backgroundColor ?? "none") // NSView's background hasn't been set yet = nil
myView.backgroundColor = .red // set NSView's background color to red color
print(myView.backgroundColor ?? "none")
view.addSubview(myView)
I went through all of these answers and none of them worked for me unfortunately. However, I found this extremely simple way, after about an hour of searching : )
myView.layer.backgroundColor = CGColorCreateGenericRGB(0, 0, 0, 0.9);
Best Solution :
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.wantsLayer = YES;
}
return self;
}
- (void)awakeFromNib
{
float r = (rand() % 255) / 255.0f;
float g = (rand() % 255) / 255.0f;
float b = (rand() % 255) / 255.0f;
if(self.layer)
{
CGColorRef color = CGColorCreateGenericRGB(r, g, b, 1.0f);
self.layer.backgroundColor = color;
CGColorRelease(color);
}
}
In Swift:
override func drawRect(dirtyRect: NSRect) {
NSColor.greenColor().setFill()
NSRectFill(dirtyRect)
super.drawRect(dirtyRect)
}
Use NSBox, which is a subclass of NSView, allowing us to easily style
Swift 3
let box = NSBox()
box.boxType = .custom
box.fillColor = NSColor.red
box.cornerRadius = 5
Without doubt the easiest way, also compatible with Color Set Assets:
Swift:
view.setValue(NSColor.white, forKey: "backgroundColor")
Objective-C:
[view setValue: NSColor.whiteColor forKey: "backgroundColor"];
Interface Builder:
Add a user defined attribute backgroundColor in the interface builder, of type NSColor.
Just set backgroundColor on the layer (after making the view layer backed).
view.wantsLayer = true
view.layer?.backgroundColor = CGColor.white
I tested the following and it worked for me (in Swift):
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.blackColor().colorWithAlphaComponent(0.5).CGColor
In Swift 3, you can create an extension to do it:
extension NSView {
func setBackgroundColor(_ color: NSColor) {
wantsLayer = true
layer?.backgroundColor = color.cgColor
}
}
// how to use
btn.setBackgroundColor(NSColor.gray)
In swift you can subclass NSView and do this
class MyView:NSView {
required init?(coder: NSCoder) {
super.init(coder: coder);
self.wantsLayer = true;
self.layer?.backgroundColor = NSColor.redColor().CGColor;
}
}
This supports changing systemwide appearance (turning dark mode on or off) while the application is running. You can also set the background colour in Interface Builder, if you set the class of the view to BackgroundColorView first.
class BackgroundColorView: NSView {
#IBInspectable var backgroundColor: NSColor? {
didSet { needsDisplay = true }
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
wantsLayer = true
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
wantsLayer = true
}
override var wantsUpdateLayer: Bool { return true }
override func updateLayer() {
layer?.backgroundColor = backgroundColor?.cgColor
}
}
Have a look at RMSkinnedView. You can set the NSView's background color from within Interface Builder.
Just small reusable class (Swift 4.1)
class View: NSView {
var backgroundColor: NSColor?
convenience init() {
self.init(frame: NSRect())
}
override func draw(_ dirtyRect: NSRect) {
if let backgroundColor = backgroundColor {
backgroundColor.setFill()
dirtyRect.fill()
} else {
super.draw(dirtyRect)
}
}
}
// Usage
let view = View()
view.backgroundColor = .white

Hide NSWindow title bar

Is there a way to hide the titlebar in an NSWindow? I don't want to have to completely write a new custom window. I can't use NSBorderlessWindowMask because I have a bottom bar on my window, and using NSBorderlessWindowMask makes that disappear. I also tried using setContentBorderThickness:forEdge: with NSMaxYEdge and setting it to 0, that didn't work either.
Any help is appreciated
[yourWindow setStyleMask:NSBorderlessWindowMask];
Starting from OS X 10.10, you can hide title bar.
window1.titlebarAppearsTransparent = true
window1.titleVisibility = .Hidden
Maybe you want to override window style.
window1.styleMask = NSResizableWindowMask
| NSTitledWindowMask
| NSFullSizeContentViewWindowMask
Kind of Welcome screen NSWindow / NSViewController setup (Swift 4.1)
extension NSWindow {
enum Style {
case welcome
}
convenience init(contentRect: CGRect, style: Style) {
switch style {
case .welcome:
let styleMask: NSWindow.StyleMask = [.closable, .titled, .fullSizeContentView]
self.init(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: true)
titlebarAppearsTransparent = true
titleVisibility = .hidden
standardWindowButton(.zoomButton)?.isHidden = true
standardWindowButton(.miniaturizeButton)?.isHidden = true
}
}
}
class WelcomeWindowController: NSWindowController {
private (set) lazy var viewController = WelcomeViewController()
private let contentWindow: NSWindow
init() {
contentWindow = NSWindow(contentRect: CGRect(x: 400, y: 200, width: 800, height: 472), style: .welcome)
super.init(window: contentWindow)
let frameSize = contentWindow.contentRect(forFrameRect: contentWindow.frame).size
viewController.view.setFrameSize(frameSize)
contentWindow.contentViewController = viewController
}
}
class WelcomeViewController: NSViewController {
private lazy var contentView = View()
override func loadView() {
view = contentView
}
init() {
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
contentView.backgroundColor = .white
}
}
class View: NSView {
var backgroundColor: NSColor?
convenience init() {
self.init(frame: NSRect())
}
override func draw(_ dirtyRect: NSRect) {
if let backgroundColor = backgroundColor {
backgroundColor.setFill()
dirtyRect.fill()
} else {
super.draw(dirtyRect)
}
}
}
Result
What happens if you get the superview of the close button? Can you hide that?
// Imagine that 'self' is the NSWindow derived class
NSButton *miniaturizeButton = [self standardWindowButton:NSWindowMiniaturizeButton];
NSView* titleBarView = [miniaturizeButton superview];
[titleBarView setHidden:YES];
The only way I know would be to create a window without a titlebar (see
NSBorderlessWindowMask). Note that you can't (easily) create a window without a
titlebar in IB, so you will have to do a bit of work in code (there are a
couple of different approaches, you can probably figure it out).
A big drawback with using a window without a titlebar is that you're now on the
hook for much more of the standard appearance and behaviour - rounded corners
and such.
I had an experience that when I first set content view of my window and then set the window borderless:
[yourWindow setStyleMask:NSBorderlessWindowMask];
Nothing would appear in my window. So i first set the style mask and after that i've set the content view:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// 1. borderless window
[[self window] setStyleMask: NSBorderlessWindowMask];
// 2. create the master View Controller
self.masterViewController = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
// 3. Add the view controller to the Window's content view
[self.window.contentView addSubview:self.masterViewController.view];
self.masterViewController.view.frame = ((NSView*)self.window.contentView).bounds;
}
And voila, the content of my window has appeared.
Select Window in storyboard or XIB and tick the red circled option.
You can use WAYInAppStoreWindow available on GitHub which works on Yosemite and Mavericks.
Swift
NSApp.mainWindow?.styleMask = .borderless