IS it possible to cut out parts of a NSWindow or an NSView and make them see through? I have an NSWindow with an NSView and I want to either:
A) make a hole in the NSWindow to be able to see through it or
B) set my NSWindow background to have a clear color and then make an NSView on top and set a certain part of my NSViews opacity to be able to see through to the desktop.
This is the effect I am trying to create:
Yes, it's possible, and actually not that hard.
In your window subclass, you need to set the window background color to transparent
self.backgroundColor = NSColor.clearColor;
and tell the compositing engine that parts of your window are transparent and need to be redrawn when the window moves
[self setOpaque:NO];
Setting the background color was not necessary in early versions of macOS and many answers still do not mention that fact. I've verified that at least since macOS 10.11 it is necessary.
In your NSView subclass, you must render the new background with a color of your choice (otherwise the window is entirely transparent and only the title bar will show) and then render a hole in the view with
NSRectFillUsingOperation(NSMakeRect(50, 50, 100, 100), NSCompositingOperationClear);
This gives the desired effect and also works in Mojave's dark mode etc.
Full code:
#interface MyWindow : NSWindow
- (id)initWithContentRect:(NSRect)contentRect styleMask:( unsigned int)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag;
#end
#implementation MyWindow
- (id)initWithContentRect:(NSRect)contentRect styleMask:( unsigned int)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
self = [super initWithContentRect:contentRect styleMask : aStyle backing :bufferingType defer:flag ];
if (self)
{
self.backgroundColor = NSColor.clearColor;
[self setOpaque:NO];
[self setHasShadow:NO];
}
return self;
}
#end
#interface MyView : NSView
- (void)drawRect:(NSRect)rect;
#end
#implementation MyView
- (void)drawRect:(NSRect)rect
{
[[NSColor windowBackgroundColor] set];
NSRectFill(self.bounds);
NSRectFillUsingOperation(NSMakeRect(50, 50, 100, 100), NSCompositingOperationClear);
}
#end
Related
Whenever I try to create a line using NSBezierPath, it will never show. However, I know that if I were to add that line to a view and add that view to the window, it would work.
Note: I'm doing this Programmatically, not with XCode
If anyone could help that would be great.
#interface WindowView : NSView
- (void)drawRect:(NSRect)dirtyRect;
#end
#implementation WindowView
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor redColor] set];
NSRectFill(dirtyRect);
}
#end
Then I call it by:
[[WindowView alloc] drawRect:[window->handle frame]];
Note: window->handle is an instance of NSWindow, and if you were to log it, frame is an NSRect
I'm guessing I don't know enough about Quartz or CAShapeLayer manipulations, but I wanted to know how to change the fill color to custom UIView that I have.
Here's the implementation for DotView:
#define kCustomBlue [UIColor colorWithRed:181.0/255 green:228.0/255 blue:226.0/255 alpha:1]
#implementation DotView
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
self.backgroundColor = [UIColor clearColor];
self.opaque = YES;
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
if ((self = [super initWithCoder:coder])) {
self.backgroundColor = [UIColor clearColor];
self.opaque = YES;
}
return self;
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
[circleLayer setPath:[[UIBezierPath bezierPathWithOvalInRect:rect] CGPath]];
circleLayer.fillColor = kCustomBlue.CGColor;
[self.layer addSublayer:circleLayer];
}
#end
After placing a DotView object ((nonatomic, weak) IBOutlet in my ViewController interface) in my Storyboard, I run the code and everything is fine and dandy. That object in my ViewController interface is called dot1
I want to have a property #property (nonatomic, strong) UIColor* fillingColor; which sets the fill color for the view. How do I implement that correctly?
The idea is this: there is a tap gesture recognizer object attached to the dot1 view, and everytime I tap the dot, the color changes from blue to black (then black to blue).
I'm using XCode 9.3 and have an iPhone 7 running iOS 11.2
Thanks, Anthony
Add the property to the .h:
#interface DotView: UIView
// Add this to everything else you have
#property (nonatomic, strong) UIColor* fillingColor;
#end
Then in the .m, override the setter:
- (void)setFillingColor:(UIColor *)color {
_fillingColor = color;
[self setNeedsDisplay];
}
Then in drawRect:, use your fillingColor property for the fill color:
circleLayer.fillColor = self.fillingColor.CGColor;
Please note that you do not want to be adding layers over and over every time drawRect: is called. You don't even need to use layers for this. Just fill the UIBezierPath.
For a personal project, I'm currently trying to replicate the visual stylings of the toolbar in Automator for OS X. I have tried just about everything to get my NSButtons inside of the NSToolbar to look visually similar, but can't seem to figure out the delicate UI components to figure it out, so I'm turning to the brilliant minds on Stack Overflow.
What I'm trying to do: I'm trying to copy the visual stylings of the Automator toolbar buttons:
The Setup: Currently I have tiff images for active button state, inactive button state, and pressed button state. I want to use these images as the background for the NSButtonCell. Right now, I've subclassed NSButtonCell (code below), and set the NSButtonCell class to be TFToolbarButtonCell in the XIB file in Interface Builder. In the subclass of NSButtonCell, I'm overriding -drawWithFrame:inView to draw the appropriate state image in the frame; I'm also overriding -highlight:withFrame:inView to draw the pressed image when the button is clicked.
Any direction into what I might be doing wrong here would be greatly appreciated!
TFToolbarButtonCell.h
#import <Cocoa/Cocoa.h>
#interface TFToolbarButtonCell : NSButtonCell
#property (nonatomic, strong) NSImage *onImage;
#property (nonatomic, strong) NSImage *offImage;
#property (nonatomic, strong) NSImage *highlightImage;
#end
TFToolbarButtonCell.m
#import "TFToolbarButtonCell.h"
#implementation TFToolbarButtonCell
- (id)init
{
self = [super init];
if(self) {
//initialize here
}
return self;
}
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
if([self state]){
[self.onImage drawInRect:cellFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
} else {
[self.offImage drawInRect:cellFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
}
- (void)highlight:(BOOL)flag withFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
if(flag){
[self.highlightImage drawInRect:cellFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
}
#end
I think you can accomplish what you want by subclassing NSButton and implementing
- (void)mouseDown:(NSEvent *)theEvent
- (void)mouseUp:(NSEvent *)theEvent
and your own method to set the inactive state.
These methods would call
- (void)setImage:(NSImage *)anImage
To change from active to pressed to inactive states using the different images.
You also have to uncheck "Bordered" in Interface Builder on your NSButton to stop the button background from showing.
Also, calling
- (void)setEnabled:(BOOL)enabled
on the NSToolbarItem will change the palette label to active/inactive (grey the text below the button).
Is there any way to color NSPopover? Ive seen apps like facetab etc that have cool colors and resizeable popovers, how is this done?
Ay guides, hints?
Thanks.
Set popover.contentViewController.view as a subclass of NSView with a custom background drawing (i.e. override drawRect: and fill a rect with your custom background color).
Then set the popover.appearance = NSPopoverAppearanceHUD to remove the default border around the view.
Note that there will still be a very thin border around the view, so if you want to remove it completely, you may want to use MAAttachedWindow or a similar solution.
In Swift 4:
Go to File->New File->Cocoa Class
Name your class. eg. PopColor. Make sure it is a subclass of NSView
Set the contents of the file to:
import Cocoa
class PopoverContentView:NSView {
var backgroundView:PopoverBackgroundView?
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
if let frameView = self.window?.contentView?.superview {
if backgroundView == nil {
backgroundView = PopoverBackgroundView(frame: frameView.bounds)
backgroundView!.autoresizingMask = NSView.AutoresizingMask([.width, .height]);
frameView.addSubview(backgroundView!, positioned: NSWindow.OrderingMode.below, relativeTo: frameView)
}
}
}
}
class PopoverBackgroundView:NSView {
override func draw(_ dirtyRect: NSRect) {
NSColor.green.set()
self.bounds.fill()
}
}
In your storyboard, select the view which has your popover content and go to the Identity Inspector
Set the Class to PopoverContentView
Your popover and its triangle will now be green.
You can use MAAttachedWindow instead.
You can subclass NSView and set it as the NSPopover's view controller's view.
Yes and no. Unfortunately NSPopover isn't designed to be customisable. You can use some simple hacks for adding additional background view behind contentViewController's view and colorise or customise it as you want. In this case, you can get the customisable background that will be masked the same as generic NSPopover border and tail.
For more details you can take a look at the code of NSPopover+MISSINGBackgroundView category that implements this approach or just use this piece of code as CocoaPod library.
The complete code to change the color of NSPopover including the triangle is here:
I assume people have hooked the popover outlets and methods
#import "AppDelegate.h"
#interface MyPopoverBackgroundView : NSView
#end
#implementation MyPopoverBackgroundView
-(void)drawRect:(NSRect)dirtyRect
{
[[NSColor redColor] set];
NSRectFill(self.bounds);
}
#end
//===============================================================================================
#interface MyPopView : NSView
#end
#implementation MyPopView
-(void)viewDidMoveToWindow{
NSView *aFrameView = [[self.window contentView] superview];
MyPopoverBackgroundView * aBGView =[[MyPopoverBackgroundView alloc] initWithFrame:aFrameView.bounds];
aBGView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[aFrameView addSubview:aBGView positioned:NSWindowBelow relativeTo:aFrameView];
[super viewDidMoveToWindow];
}
#end
//-----------------------------------------------------------------------------------------
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
//close when clicked outside
[self.popover setBehavior:NSPopoverBehaviorTransient];
//change its color
MyPopView *myPopview = [MyPopView new];
[self.popover.contentViewController.view addSubview:myPopview];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (IBAction)closePopover:(id)sender {
[self.popover close];
}
- (IBAction)showPopover:(id)sender {
[self.popover showRelativeToRect:[sender bounds]
ofView:sender
preferredEdge:NSMaxYEdge];
}
#end
This is what I did to change the popover color.
Assuming that you have properly defined your NSPopover:
//AppController.h
#import <Foundation/Foundation.h>
#interface AppController : NSObject
#property (weak) IBOutlet NSPopover errorPopover;
// whatever else you have ...
#end
//AppController.m
#import "AppController.h"
#implementation AppController
#synthesize errorPopover = _errorPopover;
// whatever else you have ...
-(IBAction)doSomethingThatCallsPopover:(id)sender {
_errorPopover.appearance = NSPopoverAppearanceHUD; //set color of error popup
[[self errorPopover] showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxXEdge];
}
#end
NSPopover Class Reference - I really wish they would provide usage code in the developer docs.
I have this class:
Header:
#interface vcMain : NSWindowController {
IBOutlet NSView *scroll;
}
#property (retain) IBOutlet NSView *scroll;
-(IBAction)test:(id)sender;
#end
Source:
#implementation vcMain
#synthesize scroll;
-(IBAction)test:(id)sender {
vItem *item = [[vItem alloc] initWithNibName:#"vItem" bundle:nil];
NSView *view = [item view];
[view setFrame:NSMakeRect(0, 0, 300, 600)];
[view setAutoresizingMask:( NSViewHeightSizable) ];
[scroll addSubview:view];
}
#end
*scroll is a Custom View in a Bordered Scroll View in the Content View of a Window.
vItem is a ViewController subclass with some things on it to identify its position.
Problem: When resizing my vcMain from the default 300x600 to 150x300, I don't see any scrollbars.
What am I doing wrong?
Tom
Solved
It was simple, actually. Resizing the view apparently also resized the subview so it wasn't neccessary to show the scrollbars - however, as the elements in the subview didn't move, I didn't notice that the subview was resizing.
Solved by correcting the resizing of the view.