CollectionViewFlowLayout Issue - objective-c

when I am adding UICollectionViewFlowLayout file with collection view. collectionViewCell height is not increasing according to the collection view. it takes spacing from the top and bottom:
- (void)awakeFromNib
{
[super awakeFromNib];
self.itemSize = CGSizeMake((self.collectionView.frame.size.width/2)+100,self.collectionView.frame.size.height );
//self.minimumInteritemSpacing = 10.0;
self.minimumLineSpacing = 10.0;
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.sectionInset = UIEdgeInsetsMake(0, 30.0, 0, 30.0);
}
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
NSInteger itemsCount = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0];
// Imitating paging behaviour
// previous offset and scroll direction
if ((self.previousOffset > self.collectionView.contentOffset.x) && (velocity.x < 0.0f)) {
self.currentPage = MAX(self.currentPage - 1, 0);
} else if ((self.previousOffset < self.collectionView.contentOffset.x) && (velocity.x > 0.0f)) {
self.currentPage = MIN(self.currentPage + 1, itemsCount - 1);
}
// Update offset by using item size + spacing
CGFloat updatedOffset = (self.itemSize.width + 5) * self.currentPage;
self.previousOffset = updatedOffset;
return CGPointMake(updatedOffset, proposedContentOffset.y);
}
i have created sub class of UICollectionViewLayout.

Related

how to snap collection view items to one per swipe regardless of scrolling velocity

I'm using the code from the Apple demo in a subclass of UICollectionViewFlowLayout:
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
CGFloat offsetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + self.collectionView.bounds.size.width / 2.;
CGRect targetRect = CGRectMake(proposedContentOffset.x, 0., self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
UICollectionViewLayoutAttributes *targetAttributes = nil;
NSArray *attributes = [super layoutAttributesForElementsInRect:targetRect];
for (UICollectionViewLayoutAttributes *a in attributes) {
CGFloat itemHorizontalCenter = a.center.x;
if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
offsetAdjustment = itemHorizontalCenter - horizontalCenter;
targetAttributes = a;
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}
This works in the sense that whenever I swipe / pan and release, an item will snap into place. But the proposed content offset is proportional to the swipe velocity. What I'm trying to do is that, no matter how fast / slow I swipe, the collection view only snaps to the next item immediately before or after the one that's currently centered.
I tried doing this:
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
NSInteger currentPage = (int)(self.collectionView.contentOffset.x / self.collectionView.bounds.size.width);
if (proposedContentOffset.x > self.collectionView.contentOffset.x) {
currentPage = (MIN(currentPage + 1, ((int)(self.collectionView.contentSize.width / self.collectionView.bounds.size.width)) - 1));
}
proposedContentOffset.x = self.collectionView.bounds.size.width * currentPage;
CGFloat offsetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + self.collectionView.bounds.size.width / 2.;
CGRect targetRect = CGRectMake(proposedContentOffset.x, 0., self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
UICollectionViewLayoutAttributes *targetAttributes = nil;
NSArray *attributes = [super layoutAttributesForElementsInRect:targetRect];
for (UICollectionViewLayoutAttributes *a in attributes) {
CGFloat itemHorizontalCenter = a.center.x;
if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
offsetAdjustment = itemHorizontalCenter - horizontalCenter;
targetAttributes = a;
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}
But it's not quite right. Sometimes it'll skip an item and snap to the next one after that (that is, it'll not snap to the item immediately next to the one I start scrolling from, but the one next to the one immediately next).
Any thoughts?
Ok so I figured how to make it work by making everything relative to the current contentOffset:
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
if (proposedContentOffset.x > self.collectionView.contentOffset.x) {
proposedContentOffset.x = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width / 2.;
}
else if (proposedContentOffset.x < self.collectionView.contentOffset.x) {
proposedContentOffset.x = self.collectionView.contentOffset.x - self.collectionView.bounds.size.width / 2.;
}
CGFloat offsetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + self.collectionView.bounds.size.width / 2.;
CGRect targetRect = CGRectMake(proposedContentOffset.x, 0., self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
NSArray *attributes = [super layoutAttributesForElementsInRect:targetRect];
for (UICollectionViewLayoutAttributes *a in attributes) {
CGFloat itemHorizontalCenter = a.center.x;
if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
offsetAdjustment = itemHorizontalCenter - horizontalCenter;
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}

Animating UINavigationBar in iOS 7 (like Safari)

When scrolling content in Safari, the title bar animates to a smaller version of its self. What is the best way to implement this?
Currently, I am changing the size of the frame like so:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//
// Table view
//
CGFloat currentPosition = scrollView.contentOffset.y - CGRectGetHeight(self.tableView.tableHeaderView.frame) + CGRectGetHeight(self.navigationController.navigationBar.frame) + CGRectGetHeight([[UIApplication sharedApplication] statusBarFrame]);
if ([scrollView isKindOfClass:[HeadlinesHeadlinesTableView class]]) {
ScrollDirection scrollDirection;
if (self.lastContentOffset > scrollView.contentOffset.y) {
scrollDirection = ScrollDirectionDown;
} else if (self.lastContentOffset < scrollView.contentOffset.y) {
scrollDirection = ScrollDirectionUp;
}
self.lastContentOffset = scrollView.contentOffset.y;
CGRect frame = self.navigationController.navigationBar.frame;
CGFloat minimumFrameHeight = 30.0f;
CGFloat maximumFrameHeight = 44.0f;
CGFloat titleSize = [[self.navigationController.navigationBar.titleTextAttributes objectForKey:NSFontAttributeName] pointSize];
CGFloat minimumTitleHeight = 22.0f;
CGFloat maximumTitleHeight = 30.0f;
if (currentPosition > 0 && CGRectGetHeight(frame) >= minimumFrameHeight && CGRectGetHeight(frame) <= maximumFrameHeight) {
switch (scrollDirection) {
case ScrollDirectionUp:
frame.size.height--;
titleSize--;
break;
case ScrollDirectionDown:
frame.size.height++;
titleSize++;
break;
default:
break;
}
if (CGRectGetHeight(frame) <= minimumFrameHeight) {
frame.size.height = minimumFrameHeight;
}
if (CGRectGetHeight(frame) >= maximumFrameHeight) {
frame.size.height = maximumFrameHeight;
}
if (titleSize <= minimumTitleHeight) {
titleSize = minimumTitleHeight;
}
if (titleSize >= maximumTitleHeight) {
titleSize = maximumTitleHeight;
}
}
[self.navigationController.navigationBar setFrame:frame];
[self.navigationController.navigationBar setTitleTextAttributes: #{ NSFontAttributeName : [UIFont fontWithName:#"Canterbury-Regular" size:titleSize] }];
}
}
Naturally, this way is not smooth and a lot of code, not to mention the fact that I need to fade out bar button items as well.
Thanks in advance!
Take a look at AMScrollingNavbar, it already has fading support.
You can try to change font size of NavigationBar to make the title smaller.

Objective-C - If statements

Quick qeustion, my code:
if (slider2.value == 1)
{
barHeight = 100;
}
else if (slider2.value == 2)
{
barHeight = 200;
}
else if (slider2.value == 3)
{
barHeight = 300;
}
else if (slider2.value == 4)
{
barHeight = 400;
}
else if (slider2.value == 5)
{
barHeight = 500;
}
else
{
barHeight = 600;
}
When I drag the slider from 1 to 6 I see the height of the bar change. But when I move from 6 to 1 the barHeight is stuck at 600 and does not return to 100. What am I missing?
Edit ( full statement ):
- (void)valueAgeChanged:(UISlider*)slider2
{
// DRAW BARS (skeme)
CGRect frame = CGRectMake(595, barX, 80, barHeight);
orangeView= [[UIView alloc] initWithFrame:frame];
orangeView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:orangeView];
// Let slider 1 animate on set value
[slider2 setValue:((int)((slider2.value + 1) / 1.0) - 1.0) animated:NO];
if (slider2.value == 1) {
barHeight = 100;
}else if (slider2.value == 2) {
barHeight = 200;
// barX = 300;
}else if (slider2.value == 3) {
barHeight = 300;
}else if (slider2.value == 4) {
barHeight = 400;
}else if (slider2.value == 5) {
barHeight = 500;
}else if (slider2.value == 6) {
barHeight = 600;
}
}
You're adding a new orangeView every time your value is changed. So they are simply overlaying each other. The tallest view will remain on screen and when you add shorter views of the same colour you won't see them.
To fix this you could set up the orangeView property outside of your valueChanged method (maybe viewDidLoad) and add it as a subview.
Then whenever your slider updates only update the frame of your view.
e.g.
- (void)valueAgeChanged:(UISlider*)slider2
{
[slider2 setValue:((int)((slider2.value + 1) / 1.0) - 1.0) animated:NO];
if (slider2.value == 1) {
barHeight = 100;
}else if (slider2.value == 2) {
barHeight = 200;
// barX = 300;
}else if (slider2.value == 3) {
barHeight = 300;
}else if (slider2.value == 4) {
barHeight = 400;
}else if (slider2.value == 5) {
barHeight = 500;
}else if (slider2.value == 6) {
barHeight = 600;
}
self.orangeView.frame = CGRectMake(595, barX, 80, barHeight);
}
You should also move this to after your if else statement, otherwise it will only update with the previous value.
EDIT:
As rmaddy points out the if statement is a bit overkill in this instance. A switch may be more readable/efficient, but as your barHeight is simply the slider value multiplied by 100 it would be much easier to write:
- (void)valueAgeChanged:(UISlider*)slider2
{
barHeight = floorf(slider2.value) * 100;
self.orangeView.frame = CGRectMake(595, barX, 80, barHeight);
}

How to access hyperlinks in PDF documents in iphone/ipad?

How can I access hyperlinks in PDF documents on the iPhone and iPad?
I want to set links on the content of an e-book.
Use following function to put UIButtons on the links in PDF. This will draw Buttons on your UIView. You have to pass your page as parameter. Currently it will draw button with type UIButtonTypeRoundedRect you change afterwards it to custom so it will be hidden. Initially buttons may be drawn other place than link so you need to adjust x/y position according to you requirement.
-(void)drawLinks:(CGPDFPageRef)thisPage
{
CGPDFDictionaryRef pageDictionary = CGPDFPageGetDictionary(thisPage);
CGPDFArrayRef outputArray;
if(!CGPDFDictionaryGetArray(pageDictionary,"Annots", &outputArray))
{
return;
}
int arrayCount = CGPDFArrayGetCount( outputArray );
if(!arrayCount)
{
//continue;
}
for( int j = 0; j < arrayCount; ++j )
{
CGPDFObjectRef aDictObj;
if(!CGPDFArrayGetObject(outputArray, j, &aDictObj))
{
return;
}
CGPDFDictionaryRef annotDict;
if(!CGPDFObjectGetValue(aDictObj, kCGPDFObjectTypeDictionary, &annotDict)) {
return;
}
CGPDFDictionaryRef aDict;
if(!CGPDFDictionaryGetDictionary(annotDict, "A", &aDict)) {
return;
}
CGPDFStringRef uriStringRef;
if(!CGPDFDictionaryGetString(aDict, "URI", &uriStringRef)) {
return;
}
CGPDFArrayRef rectArray;
if(!CGPDFDictionaryGetArray(annotDict, "Rect", &rectArray)) {
return;
}
int arrayCount = CGPDFArrayGetCount( rectArray );
CGPDFReal coords[4];
for( int k = 0; k < arrayCount; ++k ) {
CGPDFObjectRef rectObj;
if(!CGPDFArrayGetObject(rectArray, k, &rectObj)) {
return;
}
CGPDFReal coord;
if(!CGPDFObjectGetValue(rectObj, kCGPDFObjectTypeReal, &coord)) {
return;
}
coords[k] = coord;
}
char *uriString = (char *)CGPDFStringGetBytePtr(uriStringRef);
NSString *uri = [NSString stringWithCString:uriString encoding:NSUTF8StringEncoding];
CGRect rect = CGRectMake(coords[0],coords[1],coords[2],coords[3]);
CGPDFInteger pageRotate = 0;
CGPDFDictionaryGetInteger( pageDictionary, "Rotate", &pageRotate );
CGRect pageRect = CGRectIntegral( CGPDFPageGetBoxRect( thisPage, kCGPDFMediaBox ));
if( pageRotate == 90 || pageRotate == 270 )
{
CGFloat temp = pageRect.size.width;
pageRect.size.width = pageRect.size.height;
pageRect.size.height = temp;
}
rect.size.width -= rect.origin.x;
rect.size.height -= rect.origin.y;
CGAffineTransform trans = CGAffineTransformIdentity;
trans = CGAffineTransformTranslate(trans, 0, pageRect.size.height);
trans = CGAffineTransformScale(trans, 1.0, -1.0);
rect = CGRectApplyAffineTransform(rect, trans);
UIButton *btnTmp = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // After testing put here UIButtonTypeCustom
[btnTmp addTarget:self action:#selector(urlOpen:) forControlEvents:UIControlEventTouchUpInside];
rect.origin.x = rect.origin.x + 78; //I have adjusted this as per my requirement
rect.origin.y = rect.origin.y + 108; // I have adjusted this as per my requirement
btnTmp.frame = rect;
btnTmp.titleLabel.text = uri;
[self.superview addSubview:btnTmp];
}
}

Drawing in Cocoa, wrong xy value

I'm trying to draw a bunch of rectangles in an NSView which is inside an NSScrollView. The code is working pretty well, but it seems like the computer is drawing at the wrong points.
When I use NSLog() to log the current x and y the values are right, but when I look at my view, there is extra space added before the drawing coordinate.
This is the code:
//
// CoverFlowView.m
// iWatch
//
// Created by ief2 on 03/11/10.
//
//
#import "CoverFlowView.h"
#pragma mark Defaults
#define COVER_HEIGHT2WIDTH(a) ((float)a / 1.4)
#define COVER_HEIGHT (200.0)
#define COVER_WIDTH (COVER_HEIGHT2WIDTH(COVER_HEIGHT))
#define COVER_SPACE_MIN (20.0)
#pragma mark Private Methods
#interface CoverFlowView (PrivateMethods)
- (CGFloat)coverSpaceForMoviesPerLine:(NSUInteger *)n;
- (void)frameDidChange;
- (void)updateFrame;
#end
NSColor *RandomColor() {
float red = (float)(rand() % 255) / 254.0;
float green = (float)(rand() % 255) / 254.0;
float blue = (float)(rand() % 255) / 254.0;
NSColor *theColor = [[NSColor colorWithDeviceRed:red
green:green
blue:blue
alpha:1.0] retain];
return [theColor autorelease];
}
#pragma mark Implementation
#implementation CoverFlowView
#pragma mark Init and Dealloc
- (void)awakeFromNib {
srand(time(NULL));
NSMutableArray *colors = [[NSMutableArray alloc] initWithCapacity:100];
register int i;
for(i = 0; i < 100; i++) {
[colors addObject:RandomColor()];
}
self.movies = colors;
[colors autorelease];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(frameDidChange)
name:NSViewFrameDidChangeNotification
object:self];
}
- (id)initWithFrame:(NSRect)frame movies:(NSArray *)movs {
self = [super initWithFrame:frame];
if(self != nil) {
self.movies = movs;
// FIXME: Debug color array to movie
NSMutableArray *colors = [[NSMutableArray alloc] initWithCapacity:100];
register int i;
srand((unsigned int)time(NULL));
for(i = 0; i < 100; i++) {
[colors addObject:RandomColor()];
}
}
return self;
}
- (void)dealloc {
[_movies release];
[super dealloc];
}
#pragma mark Drawing
- (void)drawRect:(NSRect)dirtyRect {
[[NSColor blackColor] setFill];
NSRectFill(dirtyRect);
NSUInteger moviesPerLine = 0;
CGFloat coverSpace = 0;
// Get the values
coverSpace = [self coverSpaceForMoviesPerLine:&moviesPerLine];
// if it's less than 1, break the drawing
if(moviesPerLine < 1) {
return;
}
// get the rows not to draw
// p = top left point's y
// h = movie cover height ->\
// s = space between -->The movies height and it's space above it
//
// p
// f(x) = -------
// h + s
double rows = ((dirtyRect.origin.y) / (COVER_HEIGHT + coverSpace));
// get the first movie that has to be drawn
// could be half-draw
//
// (NSUInteger)rows * moviesPerLine
NSUInteger index = (NSUInteger)rows * moviesPerLine;
if(!(index < [self.movies count]))
return;
// variable declaration
CGFloat curY;
CGFloat curX;
// start drawing until the line after max
// start the loop
do {
NSUInteger rowIndex;
// get the y for the movie
// x = movie index (start from 1)
// m = movies per row
// h = movie height
// s = movie space
//
// [x - (x % m)]
// f(x) = (|-----------|) * (h + s) + s
// [ m ]
// BUT: if rows == 2, rows -= 1
rowIndex = ((index + 1) - ((index + 1) % moviesPerLine)) / moviesPerLine;
((index + 1) % moviesPerLine == 0) ? (rowIndex -= 1) : (rowIndex = rowIndex);
curY = rowIndex * (COVER_HEIGHT + coverSpace) + coverSpace;
// get the x fot the movie
// x = movie index (start from 1)
// m = movies per line
// w = movie width
// s = cover space
//
//
// f(x) = ((x - (x - (x % m))) - 1) * (w + s) + s
// BUT: if row == 0, row = m
//
rowIndex = (index+1) - (((index+1) - ((index + 1) % moviesPerLine)));
(rowIndex == 0) ? (rowIndex = moviesPerLine) : (rowIndex = rowIndex);
curX = (rowIndex - 1) * (COVER_WIDTH + coverSpace) + coverSpace;
NSLog(#"%s index: %3u || x: %5.0f || y: %5.0f", __FUNCTION__, index, curX, curY);
// Start the drawing
NSRect bezierPathRect = NSMakeRect(curX + coverSpace, curY, COVER_WIDTH, COVER_HEIGHT);
NSBezierPath *path = [NSBezierPath bezierPathWithRect:bezierPathRect];
[path setLineWidth:2.0];
[[NSColor whiteColor] setStroke];
[(NSColor *)[self.movies objectAtIndex:index] setFill];
[path fill];
[path stroke];
// add up values
index++;
if(!(index < [self.movies count]))
return;
} while(curY - (COVER_HEIGHT + coverSpace) < dirtyRect.origin.y + dirtyRect.size.height);
}
- (BOOL)isFlipped {
return YES;
}
#pragma mark Private Methods
- (void)frameDidChange {
}
- (CGFloat)coverSpaceForMoviesPerLine:(NSUInteger *)n {
// x = number of covers
// w = view width
// m = cover width
// y = the space
//
// w - (mx)
// f(x) = --------
// x + 1
CGFloat m = COVER_WIDTH;
CGFloat w = [self bounds].size.width;
register NSUInteger x = 1;
CGFloat space;
while(1) {
space = ((w-(m*x)) / ( x + 1.0));
if(space < COVER_SPACE_MIN) {
x--;
space = ((w - (m*x)) / (x + 1.0));
break;
}
x++;
}
if(n) *n = x;
return space;
}
#pragma mark Properties
#synthesize movies=_movies;
- (void)setMovies:(NSArray *)ar {
if(ar != _movies) {
[_movies release];
_movies = [ar retain];
// Update frame size
NSUInteger m;
CGFloat space = [self coverSpaceForMoviesPerLine:&m];
NSUInteger lines = [ar count] / m;
CGFloat height = (COVER_HEIGHT + space) * ((CGFloat)lines) + space;
CGFloat width = [self frame].size.width;
NSRect frame = [self frame];
frame.size = NSMakeSize(width, height);
[self setFrame:frame];
[self setNeedsDisplay:YES];
}
}
#end
and here you can find a screen shot of the running application:
If someone can tell me what I'm doing wrong, I'd really appreciate it!
Thank you,
ief2
EDIT: Don't mind the wrong titles, I made mistake giving the files a name :-)
Seems to me you’re calculating the X coordinate the wrong way. You have twice the spacing at the beginning of the line and no space at the end of the line. So if you subtract one spacing from curX you get the right result:
curX = (rowIndex - 1) * (COVER_WIDTH + coverSpace);
Also your code is more complicated than it needs to be. For example, you don’t really need a loop in coverSpaceForMoviesPerLine: and the loop in drawRect: can be simplified (and made more efficient at the same time) too.