How do I embed a WebView in an NSScrollView? - cocoa

So I have a project that has some content being displayed in a WebView and I want to stick above that. I want the header to scroll with the WebView content. Now, WebView generally wants to do it's own scroll handling but you can tell it not to using:
[[webView mainFrame] setAllowsScrolling:NO];
which successfully makes the WebView's scroll bars not appear. However, despite the fact that it's embedded in an NSScrollView, the NSScrollView's scroll bars never activate. I can only conclude that the WebView needs to be told to resize, but I can't figure out how to do that. I have tried the following:
NSRect webViewBounds = [webView bounds];
[webView setFrameSize:webViewBounds.size];
but that doesn't appear to work either.
Any suggestions?

Alternative solution: What about expressing the header in HTML/CSS/JavaScript, and putting the real content in an iframe? You can traverse the frame hierarchy to find the WebFrame object for that frame, and use the DOM (perhaps wrapped by an object of your own) to control the header.

I had a quick look at the docs for this, probably a silly question; after you change the frame size are you sending a setNeedsDisplay message to the view?

The bounds and frame in your code should have the same size. Setting one to the other changes nothing.
Today I played a little bit with the WebView class. With my approach you wouldn’t have to add your header to the HTML code. Registering for a notification gives you a starting point:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(webViewDidScroll:)
name:NSViewBoundsDidChangeNotification object:nil];
Then you can check the class of the notification sender (let’s assume you have only one WebView):
- (void)webViewDidScroll:(NSNotification *)aNotification
{
if ([[aNotification object] isKindOfClass:NSClassFromString(#"WebClipView")])
{
// Do whatever you like; scroll your header etc.
}
}

Related

Getting the current URL and title from WebView

I am trying to get the current URL and title from the WebView. I've used this for the URL
- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
{
// Only report feedback for the main frame.
if (frame == [sender mainFrame]){
NSString *url = [[[[frame provisionalDataSource] request] URL] absoluteString];
[addressBar setStringValue:url];
}
}
and this for the title:
- (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame
{
// Report feedback only for the main frame.
if (frame == [sender mainFrame]){
[[sender window] setTitle:title];
}
}
This code comes straight from Apple's WebKit Objective-C Programming Guide. I only slightly modified the URL method to the addressBar instead of textField. But it doesn't work. The addressBar field is never populated with the URL of the page and the window title doesn't update ether. Everything is connected correctly in interface builder. Why won't it work?
If the page has already loaded then it is not a provisionaldatasource, replace this with
[[[[frame dataSource] request] URL] absoluteString];
Unfortunately, I myself don't know. But what I have found is a great website with tutorials one of which is to make an RSS feed and in that it takes the title from the RSS. You'll see it if your scroll down. Hope you can modify it to work!!
http://www.raywenderlich.com/2636/how-to-make-a-simple-rss-reader-iphone-app-tutorial
Here's you you get this working.
Set up your frameLoadDelegate for the WebView object. In your visual interface, control-drag from your WebView element to your NSWindow that is containing the WebView. When you release the mouse, a small black drop-down menu will appear. Select frameLoadDelegate. Once you've done this, messages such as webView:DidReceiveTitle:forFrame will be sent to the instance of your NSWindow.
Create a Subclass out of NSWindow and assign your NSWindow object to this subclass. Since this new child object will inherit everything from NSWindow, it will receive the webView:DidReceiveTitle:forFrame message.
Paste in your code above into this new child class. This effectively overrides the method definitions from the parent class, and gives you autonomous control over what happens.
Hope that helps.

How to animate a tabbarbutton image

I have a toolBarItem which calls a save action on a database. I would really much like to change the image of the item (a cabinet) dynamically so that a drawer opens, a label is animated "inside" and then it closes. Very much like the trash item on the mail app animation.
I know how to make a UIView pop-up by scaling it up and down on an animation on a given times given in an array (together with an array of CT scale), so I'm guessing it could be done more or less the same way.
Does anyone know about an example of who to accomplish that?. Back on xcode 4.1 i was able to highlight the button while the label was moving, but I cannot do that anymore (somehow I did add a normal button on the toolbarItem, which I cannot do anymore).
Thanks in advance!
well, if somebody is having the same issue here is how it can be done:
- (IBAction)barButtonAction:(UIBarButtonItem *)sender {
NSArray *frameArray=[NSArray arrayWithObjects:[UIImage imageNamed:#"01-refresh.png"], [UIImage imageNamed:#"02-redo.png"], [UIImage imageNamed:#"03-loopback.png"], [UIImage imageNamed:#"03-loopback.png"],[UIImage imageNamed:#"03-loopback.png"],[UIImage imageNamed:#"03-loopback.png"],[UIImage imageNamed:#"03-loopback.png"],[UIImage imageNamed:#"03-loopback.png"], nil];
self.button.image=[UIImage animatedImageWithImages:frameArray duration:10.0];
}

Cocoa: NSView drawRect painting over IBOutlets

I have an NSView in IB which sits above the app window. I have a subclass of NSView (AddSource) which I assign to the NSView.
On awakeFromNib I instantiate the view:
//add a new Add Source class
addSourceView = [[AddSource alloc] initWithFrame:NSMakeRect(0.0, 959.0, 307.0, 118.0)];
[[winMain contentView] addSubview:addSourceView];
in addSourceView's drawRect method I am adding a white background to the view:
[[NSColor whiteColor] set];
NSRectFill(rect);
[self setNeedsDisplay:YES];//added this to see if it might solve the problem
In winMain's contentView I have a NSButton that when clicked slides the addSourceView onto the window:
NSRect addSourceViewFrame = [addSourceView frame];
addSourceViewFrame.origin.y = 841.0;
[[addSourceView animator] setFrame:addSourceViewFrame];
But it seems as if the app is painting over the IBOutlets I placed on the NSView in IB. If, in IB, I repoistion the NSView so that it is on screen when the app launches everything works fine, the IBOutlets are there as well as the background color.
I'm not sure why this is happening. I've done this before with no problems. I must be doing something different this time.
Thanks for any help.
*note - on the 3rd screen capture, when I say this is what the app looks like when opened, that's when I hard code the Y position of the NSView. When it is functioning correctly it should open as screen capture 1.
Most likely your buttons and custom view are siblings, i.e. they are both subviews of your window's content view. Since siblings are "Stacked" depending on the order in which they are added, when you add the view in code it is being added on top of the buttons. You should be able to fix it by explicitly specifying where the view should be positioned relative to its new siblings like so:
[[winMain contentView] addSubview:addSourceView positioned:NSWindowBelow relativeTo:nil];
which should place it below any existing subviews of your window's content view. Also, remove the setNeedsDisplay: line in drawRect, that leads to unncessary, possibly infinite, redrawing.
EDIT: OK I see what you're doing.
I would suggest creating a standalove view in the NIB by dragging a "Custom View" object into the left hand side (the vertically-aligned archived objects section) and adding your controls there, that should ensure the controls are actualy subviews of the view, then you can just create a reference to the archived view in code, and add/remove it dynamically as needed.
Honestly though, you should probably be using a sheet for these kinds of modal dialogs. Why reinvent the wheel, and make your app uglier in the process?
You added TWO AddSource views to the window. You added one in IB - this view contains your textFields and buttons that are connected to the IBOutlets and it is positioned outside the window.
Then in -awakeFromNib you create another, blank AddSource view (containing nothing) and animate it into the window.
I can't recommend highly enough the Hillegass as the best introduction to IB and the correct way to build Cocoa Apps.
Also, Assertions can be useful to make sure what you think is happening is actually what is happening.
If you are certain you added a button to your view in IB, assert it is so:-
- (void)awakeFromNib {
NSAssert( myButton, #"did i hook up the outlet?");
}
NSAssert is a macro that has zero overhead in a release build.
Calling [self setNeedsDisplay:YES] from -drawRect just causes the same -drawRect to be called again. This will give you big problems.

Screen location of NSToolbarItem

How can I get the on-screen location of a button in a toolbar? That is getting the rectangle frame of an NSToolbarItem? The [NSToolbarItem view] method seems to always return nil whenever the toolbar item is only a simple action button and thus I couldn't use the normal NSView methods to pinpoint the toolbar button's on-screen position.
Background
I'm trying to use Matt Gemmell's MAAttachedWindow component to point to a specific toolbar button. The component requires an NSPoint object to "point" the user to a location on the screen.
Thanks in advance.
I happened to have the same kind of problem. Although I wouldn't say I solved it, I found a way that at least works for my scenario...
In my ToolbarItem action I fetch the current mouse location - it proved to be sufficient in this case. An example implementation might look like this:
- (IBAction)showOverlay:(id)sender {
NSPoint clickedPoint = [self.window mouseLocationOutsideOfEventStream];
self.overlayController = [[MyOverlayController alloc] initAtPoint:clickedPoint];
}

NSTextView not refreshed properly on scrolling

I have a NSTextView with a sizeable quantity of text. Whenever I scroll however, the view isn't updated properly. There are some artifacts that remain at the top or the bottom of the view. It appears that the view doesn't refresh itself often enough. If I scroll very slowly the view updates correctly though. If I add a border to the view everything works perfectly, borderless view is the one that has a problem. Here's a link to a screenshot:
Thanks
Have you set the setDrawsBackground and copiesOnScroll propertes for either the NSScrollView or the NSClipView?
The first thing I would suggest is turning off the "draws background" property of the NSScrollView:
[myScrollView setDrawsBackground:NO];
Note that this should be set on the NSScrollView, and not on the embedded NSClipView.
The following excerpt from the documentation may be relevant:
If your NSScrollView encloses an NSClipView sending a setDrawsBackground: message with a parameter of NO to the NSScrollView has the added effect of sending the NSClipView a setCopiesOnScroll: message with a parameter of NO. The side effect of sending the setDrawsBackground: message directly to the NSClipView instead would be the appearance of “trails” (vestiges of previous drawing) in the document view as it is scrolled.
Looks like the text field isn't even in the scrolling-area... Are you sure something isnt overlapping it?
I had a similar trouble - artifacts develop when the NSTextView is embedded in another scrollview (ie. a NSTableView).
I actually turned on the setdrawsbackground, and then added a nice color to make it disappear again.
-(void)awakeFromNib{
NSScrollView *scroll = [self enclosingScrollView];
[scroll setBorderType:NSNoBorder];
[scroll setDrawsBackground:YES];
[scroll setBackgroundColor:[NSColor windowBackgroundColor]];
}
This in combination with a scrollWheel event let me use the NSTextView in a NSTableView.
-(void)scrollWheel:(NSEvent *)theEvent{
NSScrollView *scroll = [self enclosingScrollView];
[[scroll superview] scrollWheel:theEvent];
}
I had the same trouble some time ago. I don't remember how I solved it.
Try to place the NSTextView to another view if the superview is a custom view. Just to see what will happen.

Resources