macOS Xcode Objective-C. Very new, so bear with me.
Displaying an image programmatically using:
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
m_imgBack = nil;
}
- (void)drawRect:(NSRect)rect {
rect = [ self bounds ];
if( m_imgBack )
[m_imgBack drawInRect:NSMakeRect(0,0,rect.size.width,rect.size.height) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
- (void)InitControls {
LoadImageFromFile( [ NSString stringWithFormat:#"%#/common/background.png", DATA_PATHNAME ], &m_imgBack );
}
This draws the background.png without a problem. However, I can not see to hide it using:
[m_imgBack setHidden:YES];
It just kicks back error: [NSImage setHidden:]: unrecognized selector sent to instance. I thought you could sethidden on an NSImage?
You can think of NSImage as a data object, so hiding it doesn't make sense just like hiding an NSArray or NSDictionary doesn't make sense.
If you're displaying the image in a view, the view can be hidden using isHidden. It's not clear from the code snippet what class the code belongs to, but assuming it's a subclass of NSView, you could try [self setHidden:YES].
Related
I'm an Objective-C beginner and I've found similar answers on stackoverflow to my question, but even after trying all of the tips/advice I'm still lost.
I have an IBOutlet UIImageView, and I want it to be so that if the user swipes up or down on this Image, then the position of various images on the view controller change.
I'm very green with the UISwipegesturerecognizer coding, so please explain in a way a beginner would understand. Thank you very much in advance!!!
UPDATE:
#Axeva, I used the following code, and there are no caution warnings or errors. However, it isn't executing properly on my simulator. Here is my code :
interface math0 (){
UISwipeGestureRecognizer *swipeUp;}
- (void)viewDidLoad
{
[super viewDidLoad];
swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget: self action: #selector(swipedScreenUp:)];
[swipeUp setNumberOfTouchesRequired: 1];
[swipeUp setDirection: UISwipeGestureRecognizerDirectionUp];
[one addGestureRecognizer: swipeUp];
[swipeUp setEnabled: NO];
}
- (void)swipedScreenUp:(UISwipeGestureRecognizer*)swipeGesture {
// Sampling to see if it works
instructions.text = [NSString stringWithFormat:#"IT WORKED!"];
}
In my .h file, I declared - (void)swipedScreenUp:(UISwipeGestureRecognizer)swipeUp;. On my storyboard, I set my image (named one) so that it was accessibility and user interaction enabled.
I've also tried with [swipeUp setEnabled: YES]; in my viewDidLoad file.
Can you or anyone tell me where the error is? Thank you!!
Try something like this:
#interface YourViewController () {
UISwipeGestureRecognizer *swipeLeftToRightGesture;
}
- (void)viewDidLoad {
[super viewDidLoad];
swipeLeftToRightGesture = [[UISwipeGestureRecognizer alloc] initWithTarget: self action: #selector(swipedScreenRight:)];
[swipeLeftToRightGesture setNumberOfTouchesRequired: 1];
[swipeLeftToRightGesture setDirection: UISwipeGestureRecognizerDirectionRight];
[[self view] addGestureRecognizer: swipeLeftToRightGesture];
}
- (void)swipedScreenRight:(UISwipeGestureRecognizer*)swipeGesture {
// Move your image views as desired
}
You can adjust this basic code to do exactly what you wish. (different swipe directions; etc.)
I'm trying to understand how best to impliment drag and drop of files from the Finder to a NSTableView which will subsequently list those files.
I've built a little test application as a proving ground.
Currently I have a single NSTableView with FileListController as it's datasourse. It's basically a NSMutableArray of File objects.
I'm trying to work out the best / right way to impliment the drag and drop code for the NSTableView.
My first approach was to subclass the NSTableView and impliment the required methods :
TableViewDropper.h
#import <Cocoa/Cocoa.h>
#interface TableViewDropper : NSTableView
#end
TableViewDropper.m
#import "TableViewDropper.h"
#implementation TableViewDropper {
BOOL highlight;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
// Initialization code here.
NSLog(#"init in initWithCoder in TableViewDropper.h");
[self registerForDraggedTypes:#[NSFilenamesPboardType]];
}
return self;
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
NSLog(#"performDragOperation in TableViewDropper.h");
return YES;
}
- (BOOL)prepareForDragOperation:(id)sender {
NSLog(#"prepareForDragOperation called in TableViewDropper.h");
NSPasteboard *pboard = [sender draggingPasteboard];
NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
NSLog(#"%#",filenames);
return YES;
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
highlight=YES;
[self setNeedsDisplay: YES];
NSLog(#"drag entered in TableViewDropper.h");
return NSDragOperationCopy;
}
- (void)draggingExited:(id)sender
{
highlight=NO;
[self setNeedsDisplay: YES];
NSLog(#"drag exit in TableViewDropper.h");
}
-(void)drawRect:(NSRect)rect
{
[super drawRect:rect];
if ( highlight ) {
//highlight by overlaying a gray border
[[NSColor greenColor] set];
[NSBezierPath setDefaultLineWidth: 18];
[NSBezierPath strokeRect: rect];
}
}
#end
The draggingEntered and draggingExited methods both get called but prepareForDragOperation and performDragOperation don't. I don't understand why not?
Next I thought I'll subclass the ClipView of the NSTableView instead. So using the same code as above and just chaging the class type in the header file to NSClipView I find that prepareForDragOperation and performDragOperation now work as expected, however the ClipView doesn't highlight.
If I subclass the NSScrollView then all the methods get called and the highlighting works but not as required. It's very thin and as expected round the entire NSTableView and not just the bit below the table header as I'd like.
So my question is what is the right thing to sublclass and what methods do I need so that when I peform a drag and drop from the Finder, the ClipView highlights properly and prepareForDragOperation and performDragOperation get called.
And also when performDragOperation is successful how can this method call a method within my FileListController telling it to create a new File object and adding it to the NSMutableArray?
Answering my own question.
It seems that subclassing the NSTableView (not the NSScrollView or the NSClipView) is the right way to go.
Including this method in the subclass :
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender {
return [self draggingEntered:sender];
}
Solves the problem of prepareForDragOperation and performDragOperation not being called.
To allow you to call a method within a controller class, you make the delagate of your NSTextView to be the controller. In this case FileListController.
Then within performDragOperation in the NSTableView subclass you use something like :
NSPasteboard *pboard = [sender draggingPasteboard];
NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
id delegate = [self delegate];
if ([delegate respondsToSelector:#selector(doSomething:)]) {
[delegate performSelector:#selector(doSomething:)
withObject:filenames];
}
This will call the doSomething method in the controller object.
Updated example project code here.
I have a mac cocoa app with a webview that contains some text. I would like to search through that text using the default find bar provided by NSTextFinder. As easy as this may seem reading through the NSTextFinder class reference, I cannot get the find bar to show up. What am I missing?
As a sidenote:
- Yes, I tried setting findBarContainer to a different view, same thing. I reverted back to the scroll view to eliminate complexity in debugging
- performTextFinderAction is called to perform the find operation
**App Delegate:**
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.textFinderController = [[NSTextFinder alloc] init];
self.webView = [[STEWebView alloc] initWithFrame:CGRectMake(0, 0, self.window.frame.size.width, 200)];
[[self.window contentView] addSubview:self.webView];
[self.textFinderController setClient:self.webView];
[self.textFinderController setFindBarContainer:self.webView.enclosingScrollView];
[[self.webView mainFrame] loadHTMLString:#"sample string" baseURL:NULL];
}
- (IBAction)performTextFinderAction:(id)sender {
[self.textFinderController performAction:[sender tag]];
}
**STEWebView**
#interface STEWebView : WebView <NSTextFinderClient>
#end
#implementation STEWebView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
}
- (NSUInteger) stringLength {
return [[self stringByEvaluatingJavaScriptFromString:#"document.documentElement.textContent"] length];
}
- (NSString *)string {
return [self stringByEvaluatingJavaScriptFromString:#"document.documentElement.textContent"];
}
In my tests, WebView.enclosingScrollView was null.
// [self.textFinderController setFindBarContainer:self.webView.enclosingScrollView];
NSLog(#"%#", self.webView.enclosingScrollView);
Using the following category on NSView, it is possible to find the nested subview that extends NSScrollView, and set that as the container, allowing the NSTextFinder to display beautifully within a WebView
#interface NSView (ScrollView)
- (NSScrollView *) scrollView;
#end
#implementation NSView (ScrollView)
- (NSScrollView *) scrollView {
if ([self isKindOfClass:[NSScrollView class]]) {
return (NSScrollView *)self;
}
if ([self.subviews count] == 0) {
return nil;
}
for (NSView *subview in self.subviews) {
NSView *scrollView = [subview scrollView];
if (scrollView != nil) {
return (NSScrollView *)scrollView;
}
}
return nil;
}
#end
And in your applicationDidFinishLaunching:aNotification:
[self.textFinderController setFindBarContainer:[self scrollView]];
To get the Find Bar to appear (as opposed to the default Find Panel), you simply have to use the setUsesFindBar: method.
In your case, you'll want to do (in your applicationDidFinishLaunching:aNotification method):
[textFinderController setUsesFindBar:YES];
//Optionally, incremental searching is a nice feature
[textFinderController setIncrementalSearchingEnabled:YES];
Finally got this to show up.
First set your NSTextFinder instances' client to a class implementing the <NSTextFinderClient> protocol:
self.textFinder.client = self.textFinderController;
Next, make sure your NSTextFinder has a findBarContainer set to the webView category described by Michael Robinson, or get the scrollview within the webView yourself:
self.textFinder.findBarContainer = [self.webView scrollView];
Set the find bar position above the content (or wherever you wish):
[self.webView scrollView].findBarPosition = NSScrollViewFindBarPositionAboveContent;
Finally, tell it to show up:
[self.textFinder performAction:NSTextFinderActionShowFindInterface];
It should show up in your webView:
Also, not sure if it makes a difference, but I have the NSTextFinder in the XIB, with a referencing outlet:
#property (strong) IBOutlet NSTextFinder *textFinder;
You may also be able to get it by simply initing it like normal: self.textFinder = [[NSTextFinder alloc] init];
I have the following code where after a bool is true I want to add a drawing to my rect. here is the code I have but for some reason it is either not setting the bool or calling the setNeedsDisplay. Am I referencing to the other class properly? thanks
//in AppController.m
-(IBAction)colorToggle:(id)sender
{
if ([colorFilter state] == NSOnState)
{
CutoutView *theView = [[CutoutView alloc] init];
[theView setFilterEnabled:YES];
}
}
//in cutoutView.m
- (void)drawRect:(NSRect)dirtyRect
{
[[[NSColor blackColor]colorWithAlphaComponent:0.9]set];
NSRectFill(dirtyRect);
//this is what i want to be drawn when my bool is true and update the drawRect
if (filterEnabled == YES) {
NSRectFillUsingOperation(NSMakeRect(100, 100, 300, 300), NSCompositeClear);
[self update];
}
}
-(void)update
{
[self setNeedsDisplay:YES];
}
OK, you know how not every UILabel is the same? Like, you can remove one UILabel from a view without all the others disappearing too? Well, your CutoutView is the same way. When you write CutoutView *theView = [[CutoutView alloc] init]; there, that creates a new CutoutView that isn't displayed anywhere. You need to talk to your existing CutoutView (probably by hooking up an outlet, but there are any number of perfectly valid designs that will accomplish this goal).
You are forgetting to call the drawRect: method, it should looks like this:
CutoutView *theView = [[CutoutView alloc] init];
[theView setFilterEnabled:YES];
[theView setNeedsDisplay];
From the docs:
When the actual content of your view changes, it is your
responsibility to notify the system that your view needs to be
redrawn. You do this by calling your view’s setNeedsDisplay or
setNeedsDisplayInRect: method of the view.
I have an interface that has an NSTextField, NSButton, and an NSView. When I type something in the NSTextfield and press the button, I want the text to be drawn in the NSView. So far I have everything connected and working, except for the view.
How can I connect the text and the view so that every time I press the button, the text is drawn to the view?
How can I connect the text and the view so that every time I press the button, the text is drawn to the view?
Views do their own drawing.
You need to give the view the string to draw, and then set the view as needing display. You'll do these in the action method that you wire the button up to.
First, your custom view class needs to have a property for the value (string, in this case) that it's going to display. From your action method, which should generally be on a controller object, send the view object a setFoo: message (assuming you named the property foo). That takes care of job one: The view now has the value to display.
Job two is even easier: Send the view a setNeedsDisplay: message, with the value YES.
That's it. The action method is two lines.
Of course, since views draw themselves, you also need your custom view to actually draw, so you need to implement the drawRect: method in that class. It, too, will be short; all you need to do is tell the string to draw itself.
Bindings
http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/CocoaBindings/Concepts/WhatAreBindings.html#//apple_ref/doc/uid/20002372-CJBEJBHH
For simplicity's sake I didn't mention this before, but the app also has a speech element to speak the string. This aspect of the program works fine, so just ignore any messages involving the SpeakAndDraw class (it's actually misnamed and only includes a speech method, nothing about drawing).
View.m
#import "View.h"
#implementation View
#synthesize stringToDraw;
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setAttributes];
stringToDraw = #"Hola";
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
NSRect bounds = [self bounds];
[self drawStringInRect:bounds];
}
- (void)setAttributes
{
attributes = [[NSMutableDictionary alloc] init];
[attributes setObject:[NSFont fontWithName:#"Helvetica"
size:75]
forKey:NSFontAttributeName];
[attributes setObject:[NSColor blackColor]
forKey:NSForegroundColorAttributeName];
}
- (void)drawStringInRect:(NSRect)rect
{
NSSize strSize = [stringToDraw sizeWithAttributes:attributes];
NSPoint strOrigin;
strOrigin.x = rect.origin.x + (rect.size.width - strSize.width)/2;
strOrigin.y = rect.origin.y + (rect.size.height - strSize.height)/2;
[stringToDraw drawAtPoint:strOrigin withAttributes:attributes];
}
#end
SpeakerController.m
#import "SpeakerController.h"
#implementation SpeakerController
- (id)init
{
speakAndDraw = [[SpeakAndDraw alloc] init];
view = [[View alloc] init];
[mainWindow setContentView:mainContentView];
[mainContentView addSubview:view];
return self;
}
- (IBAction)speakText:(id)sender
{
[speakAndDraw setStringToSay:[text stringValue]];
[speakAndDraw speak];
[view setStringToDraw:[text stringValue]];
[view setNeedsDisplay:YES];
NSLog(#"%#", view.stringToDraw);
NSLog(#"%#", [view window]);
}
#end