UIScrollView subviews and setNeedsDisplay - uiscrollview

I've the following problem :
I have a UIView containing a UIScrollView as a subview. (nib file).
Programmatically I add several subviews (UIImageView) to the UIScrollView, each UIImageview contains an image loaded from the net asynchronously, so I need to update the scrollView when the images are downloaded. In the class responsible of the images fetching, I advertise the the View controller responsible to manage the scrollView, using this code
[[(MosaicViewController *)data] scrollView setNeedsDisplay];
the Ivar data is a pointer to the ViewController.
This stuff don't work,no reload of the scrollView happen
To be sure that the call is triggered I wrote a method inside the viewController containing the scrollView, and inside this method I called setNeedsDisplay,
[(MosaicViewController *)data updateView];
-(void) updateView
{
NSLog(#"setNeedsDisplay");
[self.scrollView setNeedsDisplay];
}
the method updateView is triggered correctly, I mean is called after each Image is downloaded, but the scrollView contents isn't updated. In the ViewController containing the scrollView I don't implement the drawRect method, could be this the reason for the lack of update after calling setNeedsDisplay?
Any help/suggestion/reference etc.. is welcome
Thanks in advance

Dude are you doing imageView.image = downloadedImage ??
Also you should not have to do [self.scrollView setNeedsDisplay] since you did not change anything on scrollView !!
What changed is the content of the imageView and imageView.image = downloadedImage will automatically trigger setNeedsDisplay on imageView !!
Some other check points
Is scrollView visible?
Is scrollView.contentSize set??
Is the scrollView frame correct ??

Related

NSView inside an NSPopover: Converting Coordinates

Context
Let P be an NSPopover and view be an NSView that is a subview contained within P.
What I Need
I want to take the frame of view and convert it to the screen's coordinate system. Normally, you would do that like this:
NSRect frameRelativeToWindow = [self convertRect:self.frame relativeToView:nil];
[self.window convertRectToScreen:frameRelativeToWindow];
However, because view is in an NSPopover, its window property is nil and the above method will return NSZeroRect: [0, 0, 0, 0]
So, how can I get my view's frame in screen coordinates if the view is in an NSPopover?
Notes:
The NSPopover IS onscreen and visible.
The view whose frame I am trying to convert to screen coordinates is not a direct subview of the NSPopover or its contentView. The view in question is actually a custom NSView within an NSTableCellView within an NSTableView within an NSScrollView within an NSClipView. The clipView is a direct subview of the NSPopover's contentView.
In a normal NSWindow, you can take any subview and call [self window] to get the containing NSWindow object. Doing that in a popover, however, returns nil.
The following code assumes, that you have
a NSViewController subclass that manages the popover view
the view controller implements NSPopoverDelegate
As hamstergene pointed out, the popover's window won't be available until it is shown.
When the popoverWillShow: delegate method gets called, your popover view's window should be an instance of _NSPopoverWindow (and not nil).
- (void)popoverWillShow:(NSNotification *)notification
{
NSWindow* popOverWindow = self.view.window;
NSRect popoverRectInScreenCoords = [popOverWindow convertRectToScreen:self.view.frame];
NSLog(#"Popover Rect in Screen Coords:%#", NSStringFromRect(popoverRectInScreenCoords));
}
Update
Bryan (the OP) posted a solution to his problem in the comments.
The issue wasn't NSPopover itself. The actual reason for the nil window for some of his subviews was NSTableView, which releases non-visible NSTableCellViews.
As a workaround he implemented viewDidMoveToWindow in his custom NSTableCellView subclass. When viewDidMoveToWindow: gets called, the table cell view already has a window that can be used to perform coordinate conversion.
Your approach for calculating screen coordinates is correct (just don't forget to convert view's frame coordinates to window's base coordinates first using convertRect:self.frame toView:nil).
The problem is that the window is not created until the popover is displayed. Before that, there is no way to find the view's screen coordinates, because it is not on a screen.
If waiting for display is too late for you, catch popoverWillShow: delegate method (or the corresponding notification), which fires right before displaying the popover when the window is already created and positioned.

nssplitview programmatically add nsview on top

I am trying to programmatically add a NSView over a NSSplitView (to cover it). Every attempt to do this has resulted in it being added into the NSSplitview as an extra subview.
Can anyone please help?
Codes:
InfoTrainView *myView = [[[InfoTrainView alloc] initWithFrame: aFrame] autorelease];
[NSBundle loadNibNamed:#"InfoTrainView" owner:myView];
[self.windowController.splitViewBase addSubview:myView];
I believe you need to add your new view as a child to the NSSplitView's superview (ie. parent). This way it becomes a sibling to the NSSplitView and can cover it. Your current method makes the new view a child of the split view, which then sets itself up as splitting four ways.
NSView* parentView = [self.windowController.splitViewBase superview];
if (parentView)
[parentView addSubview:myView];
parentView above should get you the "content view" that is the default NSView inside of an NSWindow, which IB placed the NSSplitView inside. If parentView == nil, you can try manually adding an NSView to the window first, then putting your NSSplitView inside of that.
On a side note, your question answered my own question - how to programmatically add to the views split inside of an NSSplitView! Thanks :)

Making a UILabel subview in UIScrollView to be firstResponder

I have added a label with some text and links to an ScrollView,
when you tap on those links (in the label), a delegate method will call, displays a popover and show some related information.
but problem starts from here, I want when you tap on anywhere else except the links, the popover disappear.
if I add a UITapGestureRecognizer to ScrollView, the delegate method for links won't call.
what should I do to label handles the tap on links, and ScrollView Handles the other taps?
I did like that:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped)];
[self.scrollView addGestureRecognizer:tap];
- (void)tapped
{
if ([self.storyText.delegate respondsToSelector:#selector(attributedLabel:shouldFollowLink:)])
[self.storyText.delegate performSelector:#selector(attributedLabel:shouldFollowLink:) withObject:self.storyText];
}
In tapped method Im checking if Im tapping on a link, the delegate should be called, but Delegate wont call.
Am I missing someThing?
You can use custom UIButtons instead of labels or you can place custom UIButtons with clear background color over the links and give actions to this buttons.
Solved!
Solution:
first I create a custom class for my scrollView and subclassed that from UIScrollView.
Second I override
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
in my custom ScrollView class. in this method I called a method to dismiss the popover.
but important part is that in viewWillAppear method of the class which popover will appear, I passed self to custom scrollView class. because if I didn't that, the method for dismissing popover won't work (it needs an object of this class).
[self.scrollView initWithStoryViewController:self];
this images show in which scenario I had this problem:

NSView inside NSScrollview won't change size

I've made an NSWindow in Interface Builder. Inside this window is an NSScrollView and inside that is a custom NSView.
The NSScrollview fills the NSWindow and the custom NSView fills the NSScrollview.
When the custom NSView is sent the awakeFromNib method its bounds are 0,0 and 256x373 as I'd expect, filling the scrollview.
However later I change the size of the NSView to be larger than 373high but it never changes size in the scrollview.
I've tried setting the frame, I've tried setting the bounds, but nothing makes it change.
Except, when I tried changing the intrinsicSize of the custom NSView it did change, but it made the NSWindow and NSScrollview change sizes as well to fit the new size of 256x1452
Can anyone tell me where I might be going wrong?
Is it something to do with the constraints set on the Scrollview or the NSView? I haven't set any but when I added the items in Interface Builder they were automatically added for me
[EDIT]
I've changed it so that the custom NSView is created programmatically and added to the NSScrollView with setDocumentView: and everything works as I expect. So I guess technically I've solved the problem, but I'd still like an explanation on why it's not working via Interface Builder if anyone knows.
I have a partial solution, which also causes me to pose an additional question. I had a similar issue, I needed to programatically change the size of a view embedded in a NSScrolView.
This code works, need both methods
-(void)markViewSizeChanged /* Works correctly */
{
[self setFrameSize:currentViewSize];
[self setBoundsSize:currentViewSize];
[self setNeedsDisplay:YES];
}
-(NSSize)intrinsicContentSize // Override of class method
{
return currentViewSize;
}
Note: MUST set currentViewSize in awakeFromNib
Now for the curious part. If I reverse the order of the two calls setting the frame and bounds, the size of the embedded view is correct, but the scaling factor of objects drawn is off.
-(void)markViewSizeChanged /* DOES NOT work correctly, scaling in drawing off */
{
[self setBoundsSize:currentViewSize];
[self setFrameSize:currentViewSize];
[self setNeedsDisplay:YES];
}

NSViewController and multiple subviews from a Nib

I'm having a difficult time wrapping my head around loading views with Interface Builder and NSViewController.
My goal is to have a view which meets the following description: Top bar at the top (like a toolbar but not exactly) which spans the entire width of the view, and a second "content view" below. This composite view is owned by my NSViewController subclass.
It made sense to use Interface Builder for this. I have created a view nib, and added to it two subviews, laid them out properly (with the top bar and the content view). I've set File's Owner to be MyViewController, and connected outlets and such.
The views I wish to load in (the bar and the content) are also in their own nibs (this might be what's tripping me up) and those nibs have their Custom Class set to the respective NSView subclass where applicable. I'm not sure what to set as their File's Owner (I'm guessing MyController as it should be their owner).
Alas, when I init an instance of MyViewController none of my nibs actually display. I've added it to my Window's contentView properly (I've checked otherwise), and actually, things sort of load. That is, awakeFromNib gets sent to the bar view, but it does not display in the window. I think I've definitely got some wires crossed somewhere. Perhaps someone could lend a hand to relieve some of my frustration?
EDIT some code to show what I'm doing
The controller is loaded when my application finishes launching, from the app delegate:
MyController *controller = [[MyController alloc] initWithNibName:#"MyController" bundle:nil];
[window setContentView:[controller view]];
And then in my initWithNibName I don't do anything but call to super for now.
When breaking out each view into its own nib and using NSViewController, the typical way of handling things is to create an NSViewController subclass for each of your nibs. The File's Owner for each respective nib file would then be set to that NSViewController subclass, and you would hook up the view outlet to your custom view in the nib. Then, in the view controller that controls the main window content view, you instantiate an instance of each NSViewController subclass, then add that controller's view to your window.
A quick bit of code - in this code, I'm calling the main content view controller MainViewController, the controller for the "toolbar" is TopViewController, and the rest of the content is ContentViewController
//MainViewController.h
#interface MainViewController : NSViewController
{
//These would just be custom views included in the main nib file that serve
//as placeholders for where to insert the views coming from other nibs
IBOutlet NSView* topView;
IBOutlet NSView* contentView;
TopViewController* topViewController;
ContentViewController* contentViewController;
}
#end
//MainViewController.m
#implementation MainViewController
//loadView is declared in NSViewController, but awakeFromNib would work also
//this is preferred to doing things in initWithNibName:bundle: because
//views are loaded lazily, so you don't need to go loading the other nibs
//until your own nib has actually been loaded.
- (void)loadView
{
[super loadView];
topViewController = [[TopViewController alloc] initWithNibName:#"TopView" bundle:nil];
[[topViewController view] setFrame:[topView frame]];
[[self view] replaceSubview:topView with:[topViewController view]];
contentViewController = [[ContentViewController alloc] initWithNibName:#"ContentView" bundle:nil];
[[contentViewController view] setFrame:[contentView frame]];
[[self view] replaceSubview:contentView with:[contentViewController view]];
}
#end
Should not MainViewController be a subclass of NSWindowController? And the outlets in the class connected to view elements in the main Window in MainMenu.xib?
Let's hope old threads are still read...

Resources