I'm porting an iPhone app that uses this tutorial into a Mac app. I've been able to get text to display in NSScrollView, but I can't get the text to scroll and I hope someone knows what I am missing.
In the iPhone app, CTView is subclassed from UIScrollView
CTView.h:
#interface CTView : UIScrollView <UIScrollViewDelegate> {
1) But there is no NSScrollViewDelegate in AppKit. Is the missing delegate interface to NSScrollView the problem?
So for the Mac app, CTView.h looks like the following:
#interface CTView : NSScrollView
In CTView.m I have
[self setHasHorizontalScroller:YES];
[self setAcceptsTouchEvents:YES];
Also inside CTView.m I create a NSMutableArray of frames as
self.frames = [NSMutableArray array];
and fill the array up with frames. I also set the frame for the document view for the complete larger image that I want to be able to scroll through as:
[[self contentView] setFrame:[self bounds]];
[self.documentView setFrame: NSMakeRect(0, 0, totalPages * self.bounds.size.width, textFrame.size.height)];
The first frame is visible in the CTView, but when I move the horizontal scroll bar, the second frame doesn't show up in CTView.
I'm using Xcode 4.3.3 and Mac OS X 10.7.4.
Does anyone know what I need to do differently from UIScrollView for NSScrollView to be able to scroll through the frames?
Finally figured it out. Whew!
1) NSScrollView needs a view for its documentView, where the documentView is what is scrolled over. So in the nib I created a NSScrollView that contained a view for the documentView and set the documentView to that view as shown by the code in CTView.m
NSArray *viewArray = [self subviews];
NSArray *docViewArray = [[viewArray objectAtIndex:0] subviews];
NSView *docView = [docViewArray objectAtIndex:0];
[self setDocumentView:docView];
2) Then I added the array of frames, that would normally be added to UIScrollView, to the documentView of NSScrollView. The array of frames consists of multiple elements of content.
while (textPos < [attString length]) {
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(textPos, 0), innerPath, NULL);
CFRange frameRange = CTFrameGetVisibleStringRange(frame);
CTColumnView* content = [[[CTColumnView alloc] initWithFrame: NSMakeRect(0, 0, self.contentSize.width, self.contentSize.height)] autorelease];
[docView addSubview: content]; // <<-- Here is where the frames are added to the documentView
textPos += frameRange.length;
}
And that did the trick.
Related
I have a UITabBarController with two UITableViews, all were created in the storyboard.
The problem is that in the second tableview the first few lines of the table are under the top bar, this doesn't happens with the first tableview, even if I change the order of the views the first will work perfectly and the second will present the problem, so the one that was working perfectly now presents the same problem because is the second item of the tabbar controller.
I don't have much code to show because I didn't create the tableviews programatically.
Not sure exactly based on your description, but a couple possibilities come to mind:
Check your view controller attributes inspector in IB. Look for
"Extend Edges" option under View Controller, and uncheck "Under Top
Bars" if it is checked.
In ios7, there appears to be a behavior in UIScrollView and all
subclasses whereby the content inset and offset is automatically adjusted, sometimes not very well. You
can try disabling that either in code or IB (see link for
how: iOS 7 -- navigationController is setting the contentInset and ContentOffset of my UIScrollView)
For any newly created iOS >7.0 app I suggest you take a deeper look at autolayout. For all my old iOS 6 apps I solved it like this:
In your UITableViewController interface:
bool _hasStatusBar;
bool _hasStatusBarHeight;
UIView *_blendView;
In your UITableViewController implementation file:
-(void)viewWillAppear:(BOOL)animated{
_hasStatusBar = NO;
_blendView = nil;
[self.tableView reloadData];
}
-(void)viewWillDisappear:(BOOL)animated{
_hasStatusBar = NO;
_blendView = nil;
}
- (void) viewDidLayoutSubviews {
// WTF APPLE!?
if (!_hasStatusBar) {
int topBarOffset = 20;
_hasStatusBar = YES;
// Fix for iOS 7 overlaying status bar
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
CGRect viewBounds = self.view.bounds;
_blendView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, viewBounds.size.width, topBarOffset)];
[_blendView setBackgroundColor:COLOR_MAIN];
[_blendView setOpaque:YES];
[_blendView setAlpha:1.00];
UIView *whityMacWhite = [[UIView alloc] initWithFrame:CGRectMake(0, 0, viewBounds.size.width, topBarOffset)];
[whityMacWhite setBackgroundColor:[UIColor whiteColor]];
[whityMacWhite setOpaque:NO];
[whityMacWhite setAlpha:0.80];
[_blendView addSubview:whityMacWhite];
[self.view.superview addSubview:_blendView];
}
}
}
I need to implement a headerview with specific size and gradient. I have to insert images in certain cells of the headerview.Tried to create the cells for the headerview using the following code,but i was not able to customize the headerview.
[[tableColumn headerCell] setImage:[NSImage imageNamed:#"sampleHeader"]];
If I use the overridden subclass of headerview, I was not able to view the images or text in the header cell.Please provide me any pointers to solve this issue.
I was able to insert images and text by subclassing the NSTableHeaderCell.How to increase height of the NSTableHeaderView?
If I subclass both NSTableHeaderView and NSTableHeaderCell , was not able to view anything in the
headercell.I used the following code for setting headerview and headercell
[tableView setHeaderView:CustomHeaderView];
[tableColumn setHeaderCell:[[[CustomHeaderTableCell alloc] initImageCell:
[NSImage imageNamed:#"sample"]]autorelease]];
I have the same issue as given in the below url
http://lists.apple.com/archives/cocoa-dev/2002/Jun/msg00331.html
You don't need to subclass NSTableHeaderView.
I was able to change the height of the header view using the following snippet in the controller class:
-(void)awakeFromNib {
NSRect frame = tableView.headerView.frame;
frame.size.height = 26;
tableView.headerView.frame = frame;
}
It should be noted that the scroll view takes care of the layout. It automatically changes the frame of the headerView as necessary, but leaves the height intact. Resizing the clip view etc as suggested in the other answer is not necessary.
You can also create a NSTableHeaderView object, initialize it with a frame(rect with height and width) and set that NSTableHeaderView object to your table view.
NSTableHeaderView *tableHeaderView = [[NSTableHeaderView alloc] initWithFrame:NSMakeRect(0, 0, 120, 60)];
[myTableView setHeaderView:tableHeaderView];
[tableHeaderView release];
Following link helped me in solving the issue.
http://lists.apple.com/archives/cocoa-dev/2003/Feb/msg00676.html
You need to set the Frame for NSClipView, NSTableHeaderView and the CornerView
This is how I implemented the same in Code.
for(NSView * subview in [topScrollView subviews])
{
for(NSView * subSubView in [subview subviews])
{
if([[subSubView className] isEqualToString:#"NSTableHeaderView"] && [[subview className] isEqualToString:#"NSClipView"])
{
[subSubView setFrameSize:NSMakeSize(subSubView.frame.size.width, subSubView.frame.size.height+5)];//HeaderView Frame
[subview setFrameSize:NSMakeSize(subview.frame.size.width, subview.frame.size.height+5)];//ClipView Frame
}
}
if ([[subview className] isEqualToString:#"_NSCornerView"])
{
[subview setFrameSize:NSMakeSize(subview.frame.size.width, subview.frame.size.height+5)]; //CornerView Frame
}
}
I'm trying to make a UI similar to CalcBot where you can pull down the view controller with your finger and it reveals a hidden background view.
I think this is done with the UIScrollView but cannot work out the exact method to have for example 2x Horizontal views with one of them having a vertical.
I do have the horizontal scroll working fine, i'm just stuck with how I can make screen 1 pull down with a vertical scroller.
Any tips or pointers would be appreciated.
Thanks Aaron
You can have nested scrollviews to get scrolling working in both directions. Let's have a vertical scroller inside a horizontal scroller (though you can very well do it the other way round too), so views 3 and 1 get into a vertical scrollview, which in turn gets inside a horizontal scroller along with view 2.
You could create such a view hierarchy in code (probably in the main viewcontroller's viewDidLoad). The following snippet assumes view1, view2 and view3 as webviews.
CGRect bounds = self.view.bounds;
// main guy is a horizontal scroller
UIScrollView *hScrollView = [[UIScrollView alloc] initWithFrame:bounds];
hScrollView.contentSize = CGSizeMake(bounds.size.width * 2, bounds.size.height);
hScrollView.delegate = self;
[self.view addSubview:hScrollView];
// the horizontal scroller contains a vertical scroller
UIScrollView *vScrollView = [[UIScrollView alloc] initWithFrame:bounds];
vScrollView.contentSize = CGSizeMake(bounds.size.width, bounds.size.height * 2);
vScrollView.delegate = self;
[hScrollView addSubview:vScrollView];
// add view3 and view1 to the vertical scroller
UIWebView *view3 = [[UIWebView alloc] initWithFrame:bounds];
[view3 loadHTMLString:#"<h1>3</h1>" baseURL:nil];
[vScrollView addSubview:view3];
UIWebView *view1 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, 0, bounds.size.height)];
[view1 loadHTMLString:#"<h1>1</h1>" baseURL:nil];
[vScrollView addSubview:view1];
vScrollView.contentOffset = CGPointMake(0, bounds.size.height);
// add view2 to the horizontal scroller
UIWebView *view2 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, bounds.size.width, 0)];
[view2 loadHTMLString:#"<h1>2</h1>" baseURL:nil];
[hScrollView addSubview:view2];
// enable paging in both directions
hScrollView.pagingEnabled = TRUE;
vScrollView.pagingEnabled = TRUE;
That would have you scrolling in both directions but that would also let you scroll-right from view3 to view2. If you want to prevent that, you should disable scrolling on hScrollView whenever the vScollView is showing view3. You can do that in scrollViewDidEndDecelerating: in the delegate of vScrollView. Something like:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.verticalScrollView) {
self.horizontalScrollView.scrollEnabled = (self.verticalScrollView.contentOffset.y > self.view.bounds.origin.y);
}
}
I crawled all the web, but nothing.
I have a for loop to create uiimageview inside a uiscrollview controller.
It's a collection of images that you can scroll horizontally only.
for (int i = 0; i < NIMAGES; i++) {
NSString *filename = [NSString stringWithFormat:#"image%d.png", i+1];
ScrollerImage *iv = [[ScrollerImage alloc] initWithNibName:#"ScrollerImage" bundle:nil];
[iv initWithImage:[UIImage imageNamed:filename]];
iv.frame = CGRectMake(i * 320.0f, 0.0f, 320.0f, 150.0f);
iv.exclusiveTouch = YES;
iv.userInteractionEnabled = YES;
[sv addSubview:iv];
[iv release];
}
The ScrollerImage class is an UIImageView controller
#import <UIKit/UIKit.h>
#interface ScrollerImage : UIImageView {
}
#end
Now, how can I drag (not necessary a move, also a copy of view, or an id of view, anything that permit me to identify the image) an image from the scroller to another view?
It's like a shopping cart, where you can drag items at the bottom view (cart).
Like this:
This is an alternative, using UIGraphicsGetImageFromCurrentImageContext()
You can grab an image from currentScreen on click or after some events. Next renderize it to screen and apply a Pan gesture to move around the screen.
UIView *_DraggableView;
UIImageView *_imgvcChild1;
sample code:
- ( void ) onScrollviewClickOrOtherEvent
{
UIGraphicsBeginImageContext ( _DraggableView.frame.size );
[ _DraggableView.layer renderInContext:UIGraphicsGetCurrentContext() ];
UIImage *grab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if ( _imgvcChild1 ) [ _imgvcChild1 removeFromSuperview ];
_imgvcChild1 = [[ UIImageView alloc ] initWithImage:grab ];
_imgvcChild1.frame = _DraggableView.frame;
_imgvcChild1.userInteractionEnabled = YES;
[ self.view addSubview:_imgvcChild1 ];
UIPanGestureRecognizer *pan = [[ UIPanGestureRecognizer alloc ] initWithTarget:self action:#selector(moveObject:) ];
[ pan setMinimumNumberOfTouches:1 ];
[ _imgvcChild1 addGestureRecognizer:pan ];
}
- (void) moveObject:(UIPanGestureRecognizer *)pan
{
_imgvcChild1.center = [ pan locationInView:_imgvcChild1.superview ];
}
--- EDIT ---
A sample project on github: https://github.com/elpsk/Shopping-Cart
I've been trying to implement this in an app of mine and I had to just give up. I subclassed UIImageview like you have as well(which is a good option) and all my images are on a UIView that is a subclass of UIScrollView. You can pass your viewcontroller to the images themselves and change which view the imageview is a subview to when it reaches a certain boundary (I overrided the touchesMoved on the ImageViews themselves). The problem with this is it doesn't drag continously. The user has to literally select the image again when they dragged the image across a certain point because you're literally switching the uiimageview from one view to another.
I hope this gives you some insight at least, I really haven't found a solution to this issue. You'd think a lot of IPad apps would be paving the way to drag images all over different views and more tutorials appear on the issue!
I've created an NSScrollView which itself contains a NSClippedView as content view (this is all default, created by IB). Inside the contents view there is the (default) document view.
This NSScrollView has horizontal scroller disabled and vertical enabled and, most importantly auto hide scrollers enabled.
When I add new views (via code, runtime) to the document view the scroller does not unhide automatically, until the moment I vertically resize the window (and which in turn resizes the scrollview as well). 1px is enough. Just the new painting of the window seems enough.
What I am looking for is triggering this by code: so when I add views to the scrollviews' content view I would like the scrollbar to appear.
int numberOfChildViews = 10; //hard coded for example here
int childViewHeight = 80; //same as above
NSRect rect = NSMakeRect(0, 0, [[self.scrollView contentView] bounds].size.width, [numberOfChildViews*childViewHeight);
[[self.scrollView documentView] setFrame:rect];
[[self.scrollView documentView] setBounds:rect]; //just added to make sure
Then I added the custom views to the document view, like:
for (int i=0; i<numberOfChildViews; i++) {
NZBProgressViewController *item = [nzbProgressArray objectAtIndex:i];
int y=i*[[item view] bounds].size.height;
rect= NSMakeRect(0, y, [[scrollView contentView] frame].size.width, [[item view] bounds].size.height);
[[item view] setFrame:rect];
currentPosition++;
}
I am using a FlippedView so the origin will be displayed in left-top, like so:
#interface NSFlippedClipView : NSClipView {
}
#implementation NSFlippedClipView
- (BOOL)isFlipped {
return YES;
}
- (BOOL)isOpaque {
return YES;
}
#end
And added the following code to the awakeFromNib
NSFlippedClipView *documentView = [[NSFlippedClipView alloc] init];
[documentView setAutoresizingMask:NSViewWidthSizable];
[documentView setBackgroundColor:[self.scrollView backgroundColor]];
[self.scrollView setDocumentView:documentView];
[documentView release];
The scrollbars should become visible as soon as the document view is resized to be larger than the current viewport of the scroll view. Are you resizing the document view when you add your subviews to it?
Ahh, it's my own bad. For future reference: if you want to move the origin (0,0) to left-top use a NSView instead of NSClippedView extended class with IsFlipped method overriden to YES.
Thanks irsk for answering.