I have to achieve something like this(please check attached ScreenShot). The best possible solution which came to my mind is UICollectionView
. I have created rounded border along the cell and put a button on the cell. Now for the dotted straight line I have used CAShapeLayer
with BezierPath
and added the layer to my collectionview background with background color set to clear color
. Here comes the problem, I am not seeing any dotted line. Here is what I have tried. Now I have couple of questions. While answering please consider me as a beginner.
1) Why my lines are not showing.
2) How to calculate the width between the two cell, right now i am setting a random number.
3) Is there any other approach which can solve this use-case more efficiently.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UIBezierPath *borderPath;
CGFloat borderWidth;
KKCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"testCell" forIndexPath:indexPath];
borderPath = [UIBezierPath bezierPath];
[borderPath moveToPoint:CGPointMake(cell.frame.origin.x + cell.frame.size.width -1 , cell.frame.origin.y + 2)];
[borderPath addLineToPoint:CGPointMake(cell.frame.origin.x + cell.frame.size.width + 50, cell.frame.origin.y +2)];
borderWidth = 1.0;
[self dottedLineWithPath:borderPath withborderWidth:borderWidth];
return cell;
}
/** creating dotted line **/
- (void)dottedLineWithPath:(UIBezierPath *)path withborderWidth:(CGFloat)lineWidth
{
CAShapeLayer *shapelayer = [CAShapeLayer layer];
shapelayer.strokeStart = 0.0;
shapelayer.strokeColor = [UIColor blackColor].CGColor;
shapelayer.lineWidth = borderWidth;
shapelayer.lineJoin = kCALineJoinRound;
shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:2 ], nil];
shapelayer.path = path.CGPath;
[self.milestoneCollection.backgroundView.layer addSublayer:shapelayer];
}
Here is what I have achieved till now.
Hi done this using UICollectionViewLayout
and draw functions.
Code uploaded in path https://github.com/arunpkumar92/OrderStatusCollectionView
- (void)reloadConnections{
for (CAShapeLayer *shapelayer in self.connectionLines) {
[shapelayer removeFromSuperlayer];
}
self.connectionLines = [NSMutableArray array];
for (int i = 0; i <= (self.total-2); i++){
NSArray *paths = [self pathForconnectionLineForIndexPath:i];
for (UIBezierPath *path in paths) {
UIColor *color = (self.currentLevel > i) ? [UIColor colorWithRed:0.078 green:0.557 blue:0.067 alpha:1.00] : [UIColor colorWithRed:0.957 green:0.659 blue:0.024 alpha:1.00];
CAShapeLayer *shapelayer = [self drawdottedLineWithPath:path withborderWidth:1.0f withColor:color];
[self.collectionView.layer addSublayer:shapelayer];
[self.connectionLines addObject:shapelayer];
}
}
}
- (NSArray *)pathForconnectionLineForIndexPath:(int)index{
NSMutableArray *paths = [NSMutableArray array];
int row = index%self.noOfItems;
int column = index/self.noOfItems;
CGFloat cellWidth = self.view.frame.size.width/self.noOfItems;
CGFloat cellHeight = self.view.frame.size.width/self.noOfItems;
CGFloat connectionLength = cellHeight/2;
CGFloat leftPadding;
CGFloat topPadding;
if (row != (self.noOfItems-1)) {
leftPadding = cellWidth/4;
topPadding = cellHeight/2;
UIBezierPath *borderPath = [UIBezierPath bezierPath];
CGFloat startX = (3*leftPadding)+(row*cellWidth);
CGFloat startY = topPadding+(column*cellHeight);
CGFloat endX = startX + connectionLength;
CGFloat endY = startY;
[borderPath moveToPoint:CGPointMake(startX , startY)];
[borderPath addLineToPoint:CGPointMake(endX, endY)];
[self drawDirectionAt:CGPointMake((startX+(connectionLength/2)) , startY) isEnabled:(self.currentLevel > index) isRight:YES];
[paths addObject:borderPath];
}
if ( (row == (self.noOfItems-1)) && (column%2 == 0) ) {
leftPadding = cellWidth/2;
topPadding = cellHeight/8;
UIBezierPath *borderPath = [UIBezierPath bezierPath];
CGFloat startX = leftPadding+(row*cellWidth);
CGFloat startY = (7*topPadding)+(column*cellHeight);
CGFloat endX = startX;
CGFloat endY = startY + (2*topPadding);
[borderPath moveToPoint:CGPointMake(startX , startY)];
[borderPath addLineToPoint:CGPointMake(endX, endY)];
[self drawDirectionAt:CGPointMake(startX , (startY+topPadding)) isEnabled:(self.currentLevel > index) isRight:NO];
[paths addObject:borderPath];
}
if ( (row == 0) && (column%2 != 0) ) {
leftPadding = cellWidth/2;
topPadding = cellHeight/8;
UIBezierPath *borderPath = [UIBezierPath bezierPath];
CGFloat startX = leftPadding+(row*cellWidth);
CGFloat startY = (7*topPadding)+(column*cellHeight);
CGFloat endX = startX;
CGFloat endY = startY + (2*topPadding);
[borderPath moveToPoint:CGPointMake(startX , startY)];
[borderPath addLineToPoint:CGPointMake(endX, endY)];
[self drawDirectionAt:CGPointMake(startX , (startY+topPadding)) isEnabled:(self.currentLevel > index) isRight:NO];
[paths addObject:borderPath];
}
return paths;
}
- (CAShapeLayer *)drawdottedLineWithPath:(UIBezierPath *)path withborderWidth:(CGFloat)lineWidth withColor: (UIColor *)color
{
CAShapeLayer *shapelayer = [CAShapeLayer layer];
shapelayer.strokeStart = 0.0;
shapelayer.strokeColor = color.CGColor;
shapelayer.lineWidth = lineWidth;
shapelayer.lineJoin = kCALineJoinRound;
shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:15],[NSNumber numberWithInt:10 ], nil];
shapelayer.path = path.CGPath;
return shapelayer;
}
- (void)drawDirectionAt:(CGPoint)point isEnabled:(BOOL)enabled isRight:(BOOL)isRight{
NSString *imageName;
imageName = isRight ? @"right-arrow" : @"down-arrow";
imageName = [NSString stringWithFormat:@"%@-%@",imageName,(enabled ? @"enabled" : @"disabled")];
CAShapeLayer* direction = [CAShapeLayer layer];
direction.opacity = 1.0;
direction.frame = CGRectMake(0, 0, 16, 16);
direction.position = point;
[direction setContents:(id)[[UIImage imageNamed: imageName] CGImage]];
[self.connectionLines addObject:direction];
[self.collectionView.layer addSublayer: direction];
}
Why my lines are not showing.
To get your drawing visible, you have to add your drawing code directly to UICollectionView
instance and not to it's backgroundView
property.
[self.milestoneCollection.layer addSublayer:shapelayer];
This works 100%. Using backgroundView
may seem like the right thing to do, but they might be using it for completely something else. I doubt backgroundView
is either not visible at this time (using default configuration) or ignoring our change requests altogether.
How to calculate the width between the two cell, right now i am setting a random number.
-(void)getLineStartPoint:(CGPoint*)startPoint
andEndPoint:(CGPoint*)endPoint
fromCellAtIndexPath:(NSIndexPath*)fromIndexPath
toCellAtIndexPath:(NSIndexPath*)toIndexPath
{
UICollectionViewLayoutAttributes* fromAttributes =
[self.milestoneCollection layoutAttributesForItemAtIndexPath:fromIndexPath];
UICollectionViewLayoutAttributes* toAttributes =
[self.milestoneCollection layoutAttributesForItemAtIndexPath:toIndexPath];
*startPoint = fromAttributes.center;
*endPoint = toAttributes.center;
}
UICollectionViewLayoutAttributes
is 100% feature rich in geometrical information about cells, headers, footers, decorationViews
at a particular indexPath
. For simplicity, I'm now using center
. I believe you need to use your logic with frame
property.
Is there any other approach, which can solve this use-case more efficiently.
YES, there are always two ways.
Of course RIGHT is different according to the situation and time available.
Be careful with indexPath
availability or you would very easily crash. Here's a simplest form of guard for this-
NSInteger numberOfItems = [self collectionView:collectionView numberOfItemsInSection:indexPath.section];
if(indexPath.item < numberOfItems-1)
{
CGPoint startPoint, endPoint;
NSIndexPath* toIndexPath =
[NSIndexPath indexPathForItem:indexPath.item+1 inSection:indexPath.section];
[self getLineStartPoint:&startPoint
andEndPoint:&endPoint
fromCellAtIndexPath:indexPath
toCellAtIndexPath:toIndexPath];
[borderPath moveToPoint:startPoint];
[borderPath addLineToPoint:endPoint];
borderWidth = 1.0;
[self dottedLineWithPath:borderPath withborderWidth:borderWidth];
}
Also when you try to scroll it (if needed), layers/paths are added every time you create a cell (this results in dark dotted lines due to multiple times adding in the layer hierarchy), you need to take care of this when reusing cells. Have a look at this :-
That should answer all of your questions. Best Of Luck!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With