I am new to programming, I am using FScalendar and I want to change days name into two alphabets i.e. (SU,MO,TU.....), and I want to remove the gaps between cells?
I have tried in given area but not found the solution:
#property (weak, nonatomic) FSCalendarAppearance *appearance;
I have attached image for reference in which gaps are visible clearly.
enter image description here
Does this code need to be changed to get zero gap between cells???????
// Calculate item widths and lefts
free(self.widths);
self.widths = ({
NSInteger columnCount = 7;
size_t columnSize = sizeof(CGFloat)*columnCount;
CGFloat *widths = malloc(columnSize);
CGFloat contentWidth = self.collectionView.fs_width - self.sectionInsets.left - self.sectionInsets.right;
FSCalendarSliceCake(contentWidth, columnCount, widths);
widths;
});
free(self.lefts);
self.lefts = ({
NSInteger columnCount = 7;
size_t columnSize = sizeof(CGFloat)*columnCount;
CGFloat *lefts = malloc(columnSize);
lefts[0] = self.sectionInsets.left;
for (int i = 1; i < columnCount; i++) {
lefts[i] = lefts[i-1] + self.widths[i-1];
}
lefts;
});
// Calculate item heights and tops
free(self.heights);
self.heights = ({
NSInteger rowCount = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek ? 1 : 6;
size_t rowSize = sizeof(CGFloat)*rowCount;
CGFloat *heights = malloc(rowSize);
if (!self.calendar.floatingMode) {
CGFloat contentHeight = self.collectionView.fs_height - self.sectionInsets.top - self.sectionInsets.bottom;
FSCalendarSliceCake(contentHeight, rowCount, heights);
} else {
for (int i = 0; i < rowCount; i++) {
heights[i] = self.estimatedItemSize.height;
}
}
heights;
});
free(self.tops);
self.tops = ({
NSInteger rowCount = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek ? 1 : 6;
size_t rowSize = sizeof(CGFloat)*rowCount;
CGFloat *tops = malloc(rowSize);
tops[0] = self.sectionInsets.top;
for (int i = 1; i < rowCount; i++) {
tops[i] = tops[i-1] + self.heights[i-1];
}
tops;
});
source file ------>. FSCalendarCollectionViewLayout.m
You can do this by adding a subview in fscalendar for weekDays. I ADDED SINGLE LETTER BUT YOU CAN ADD according to your requirements.
1- create View
2-
3-
let week_days_view = weekDaysView.instanceFromNib() as! weekDaysView
week_days_view.frame = self.fsCalendar.calendarWeekdayView.frame
self.fsCalendar.calendarWeekdayView.addSubview(week_days_view)
#ABTech Regarding this I want to change days name into two alphabets i.e. (SU,MO,TU.....), Try with modification on FSCalendarWeekdayView.m file on it's - (void)configureAppearance method on FSCalendar library.In Swift check here
NSString * str = [weekdaySymbols[index] uppercaseString];
label.text = [str substringToIndex:2];
And Regarding this I want to remove the gaps between cells?, Try this like below, Yes you need to try this #property (weak, nonatomic) FSCalendarAppearance *appearance;
calendar.appearance.titleOffset = CGPoint.init(x: -50, y: 0.0)
calendar.appearance.eventOffset = CGPoint.init(x: -50, y: 0.0)
Related
I'm trying to implement universal method for generating (drawing) grid formed with UIViews (or other controls). They all should have borders and my problem is I cannot find right algorithm to shift (combine) borders (so the "table" does not have double cell borders like now) and to increase size so the views exactly match given frame (red on picture).
Following code generates picture beneath:
////////
const CGFloat kOuterBorderWidth = 0.0;
const CGFloat kInnerBorderWidth = 1.0;
NSArray *rows;
NSArray *columns = rows = #[#0.065, #0.29, #0.29, #0.29, #0.065];
NSMutableArray *cells = [self drawGridWithFrame:self.frame
columns:columns
rows:rows
outerBorderWidth:kOuterBorderWidth
innerBordersWidth:kInnerBorderWidth];
///////
and the method itself
- (NSMutableArray*)drawGridWithFrame:(CGRect)frame
columns:(NSArray *)columns
rows:(NSArray *)rows
outerBorderWidth:(CGFloat)outerBorderWidth
innerBordersWidth:(CGFloat)innerBordersWidth
{
CGFloat x = 0, y = 0;
NSMutableArray *cells = [NSMutableArray new];
CGFloat height = [rows[0] floatValue] * frame.size.height;
for (int j = 0; j < columns.count; j++) {
CGFloat width = [columns[j] floatValue] * frame.size.width;
CGRect cellFrame = CGRectMake(x, y, width, height);
UIView *cellView = [[UIView alloc] initWithFrame:cellFrame];
cellView.layer.borderWidth = innerBordersWidth;
if (j == 0 || j == columns.count - 1) {
cellView.layer.borderColor = [UIColor magentaColor].CGColor;
}
if (j == 1) {
cellView.layer.borderColor = [UIColor orangeColor].CGColor;
}
if (j == 2) {
cellView.layer.borderColor = [UIColor brownColor].CGColor;
}
[cells addObject:cellView];
x += [columns[j] floatValue] * frame.size.width;
}
return cells;
}
Done it.
Note: outerBorderWidth is now not used here, if it is > 1.0pt, then we additionally should shift views and adjust their size
- (NSMutableArray*)drawGridWithFrame:(CGRect)frame
columns:(NSArray *)columns
rows:(NSArray *)rows
outerBorderWidth:(CGFloat)outerBorderWidth
innerBordersWidth:(CGFloat)innerBordersWidth
{
CGFloat x = 0, y = 0;
NSMutableArray *cells = [NSMutableArray new];
CGFloat heightAdjust = (CGFloat)(rows.count - 1) / (CGFloat)rows.count;
CGFloat widthAdjust = (CGFloat)(columns.count - 1) / (CGFloat)columns.count;
for (int i = 0; i < rows.count; i++) {
CGFloat height = [rows[i] floatValue] * frame.size.height + innerBordersWidth * heightAdjust;
for (int j = 0; j < columns.count; j++) {
CGFloat width = [columns[j] floatValue] * frame.size.width + innerBordersWidth * widthAdjust;
CGRect cellFrame = CGRectMake(x, y, width, height);
UIView *cellView = [[UIView alloc] initWithFrame:cellFrame];
cellView.layer.borderWidth = innerBordersWidth;
if (j == 0 || j == columns.count - 1) {
cellView.layer.borderColor = [UIColor magentaColor].CGColor;
}
if (j == 1) {
cellView.layer.borderColor = [UIColor orangeColor].CGColor;
}
if (j == 2) {
cellView.layer.borderColor = [UIColor brownColor].CGColor;
}
[cells addObject:cellView];
x += width - innerBordersWidth;
}
x = 0;
y += height - innerBordersWidth;
}
return cells;
}
Result:
I'm using Masonay to layout UI like this:
UI requirement
The size of the 9 points are the same.
And the margin between 9 points are the same.
My question is that how to make margin to left of first column point equal to the margin to the right of the last column points by using Masonay ?
My code blow is not complete.
Can anyone help me with this?
for (NSInteger i = 0; i < 9; i++) {
NSInteger row = i / 3;
NSInteger column = i % 3;
UIView * previousRowPoint;
UIView * previousColumnPoint;
UIView * firstPoint;
if (row > 0) {
previousRowPoint = hintPoints[(row - 1) * 3];
}
if (column > 0) {
previousColumnPoint = hintPoints[column - 1];
}
if (0 != i) {
firstPoint = hintPoints[0];
}
UIView * hintPoint = [[UIView alloc] init];
[hintPoints addObject:hintPoint];
hintPoint.layer.cornerRadius = 8 / 2;
hintPoint.layer.masksToBounds = YES;
hintPoint.layer.borderColor = [UIColor grayColor].CGColor;
hintPoint.layer.borderWidth = 0.5;
[self.view addSubview:hintPoint];
hintPoint.mas_key = [NSString stringWithFormat:#"hintPoint_%ld", (long)i];
[hintPoint mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(8);
make.height.mas_equalTo(8);
if (row == 0) {
make.top.equalTo(weakself.view).offset(100);
}
else {
make.top.equalTo(previousRowPoint.mas_bottom).offset(4);
}
if (column == 0) {
//TODO: how to make the trailing equal to the first point's leading
}
else if (column == 2) {
//TODO: how to make the trailing equal to the first point's leading
}
else {
make.leading.equalTo(previousColumnPoint.mas_trailing).offset(4);
}
}];
}
I have a UICollectionView in a 3x3 grid layout displaying images.
The UICollectionView has "pagingEnabled" set to YES, inorder to "page" between pages with the 3x3 grid with 9 images.
This has been accomplished using a custom UICollectionViewLayout:
#implementation HorizontalCollectionViewLayout
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (CGSize)collectionViewContentSize
{
// We should return the content size. Lets do some math:
NSInteger verticalItemsCount = (NSInteger)floorf(self.collectionView.bounds.size.height / self.itemSize.height);
NSInteger horizontalItemsCount = (NSInteger)floorf(self.collectionView.bounds.size.width / self.itemSize.width);
NSInteger itemsPerPage = verticalItemsCount * horizontalItemsCount;
NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:0];
NSInteger numberOfPages = (NSInteger)ceilf((CGFloat)numberOfItems / (CGFloat)itemsPerPage);
CGSize size = self.collectionView.bounds.size;
size.width = numberOfPages * self.collectionView.bounds.size.width;
return size;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
// Get all the attributes for the elements in the specified frame
NSArray *allAttributesInRect = [super layoutAttributesForElementsInRect:rect];
NSArray *attributesArray = [[NSArray alloc] initWithArray:allAttributesInRect copyItems:YES];
NSInteger verticalItemsCount = (NSInteger)floorf(self.collectionView.bounds.size.height / self.itemSize.height);
NSInteger horizontalItemsCount = (NSInteger)floorf(self.collectionView.bounds.size.width / self.itemSize.width);
NSInteger itemsPerPage = verticalItemsCount * horizontalItemsCount;
for (NSInteger i = 0; i < attributesArray.count; i++) {
UICollectionViewLayoutAttributes *attributes = attributesArray[i];
NSInteger currentPage = (NSInteger)floor((double)i / (double)itemsPerPage);
NSInteger currentRow = (NSInteger)floor((double)(i - currentPage * itemsPerPage) / (double)horizontalItemsCount);
NSInteger currentColumn = i % horizontalItemsCount;
CGRect frame = attributes.frame;
frame.origin.x = self.itemSize.width * currentColumn + currentPage * self.collectionView.bounds.size.width;
frame.origin.y = self.itemSize.height * currentRow;
CGRect f = UIEdgeInsetsInsetRect(frame, UIEdgeInsetsMake(2, 2, 5, 2));
attributes.frame = f;
}
return attributesArray;
}
#end
This is working as expected, as it creates a new page when the first page contains 9 images.
What I want to accomplish then, is to create a static / fixed "next page" button in the right bottom corner of grid at index 8, 17, 26, 35 ... based on the number of pages. (A button at index 8 if the collectionview only has 1 page, a button at index 8 and index 17 if the collectionview only has 2 pages ... and so on). Number of images and pages can vary based on how many images the user uploads to the app.
The following picture illustrates the idea:
Any tips on how I can accomplish this?
What logic should go into "collectionView:itemForIndexPath:"?
In your cellForItemAtIndexPath look at the indexPath.item for the number you are interested in replacing and return a collection view cell for your [ > ] button.
The way the collection view works is that you are never really concerned with the visible cells on the screen (your UICollectionViewLayout is handling that). You are only returning a cell for the indexPath you are given.
Create a dedicated UICollectionViewCell subclass just for this [ > ] cell with its own reuse identifier. It will simplify your code and you don't need to touch your current image cells.
I'm trying so create a collection view that behaves like the iOS springboard screen.
That means that I want to have a collection view, with all of my items, and to have a page controller underneath the grid (just like the springboard screen). I also want to implement the same edit mode, meaning that one my icons are in edit mode, I can drag them from one page to another, in such a way that a page doesn't have to be full.
I've tried to set a single collection view to scroll horizontally, but it doesn't act as I want. first of all, the icons are added one below the other, instead of one beside the other. secondly, I don't have the page dots this way, and the user can't put icons on different pages
I also tried to set a page controller, and put in every page the collection view. In this case, I get that while in edit mode, I can't go between screens. I guess that is because I have more that one collection view, and they don't interact with each other.
I though of another way, of using a scroll view, and try to put a collection view inside that scroll view. But it sounds to me like a very not elegant solution.
Does anyone have an idea about how to create such a scene?
Edit:
I think the answer for my problem is hiding in this question. It describes the same situation I want to have: a collection view, that is scrolled horizontally, with paging, and that the order of the icons is not flowing vertically, but also flowing horizontally. There is a partially good answer there. I say it's partially good since the answer describes a subclass of UICollectionViewFlowLayout, but in the sample project uploaded the subclass is of UICollectionViewLayout, and I think the whole point was to maintain the functions that the UICollectionViewFlowLayout has.
This question was asked a year ago, so I believe someone has an answer, or at least, I hope.
I have partial solution to my question.
I did use the answer (of Bogdan) to the linked question above. Here's the code:
layout.h:
#import <UIKit/UIKit.h>
#interface V9Layout : UICollectionViewLayout
#property (nonatomic, assign) NSInteger itemsInOneRow;
#property (nonatomic, assign) NSInteger itemsInOneCol;
#property (nonatomic, assign) CGSize itemSize;
#end
layout.m:
#import "V9Layout.h"
#interface V9Layout()
-(void)calculateLayoutProperties;
-(int)pagesInSection:(NSInteger)section;
#property (nonatomic, strong) NSMutableArray *frames;
#property (nonatomic, assign) CGFloat lineSpacing;
#property (nonatomic, assign) CGFloat interitemSpacing;
#property (nonatomic, assign) CGSize pageSize;
#end
#implementation V9Layout
#synthesize itemsInOneRow = _itemsInOneRow, itemsInOneCol = _itemsInOneCol, itemSize = _itemSize;
#synthesize lineSpacing = _lineSpacing, interitemSpacing = _interitemSpacing;
#synthesize frames = _frames;
- (id)init
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
self.itemsInOneRow = 3;
self.itemsInOneCol = 4;
self.frames = [NSMutableArray array];
}
-(void)calculateLayoutProperties {
self.pageSize = self.collectionView.bounds.size;
// The width of all line spacings is equal to width of ONE item. The same for interitem spacing
CGFloat itemWidth = self.pageSize.width / (self.itemsInOneRow + 1); // +1 because we all spaces make width of one additional item
CGFloat itemHeight = self.pageSize.height / (self.itemsInOneCol + 1); // the same
NSLog(#"calculateLayoutProperties item width: %f, item height: %f", itemWidth, itemHeight);
self.itemSize = CGSizeMake(itemWidth, itemHeight);
// this part is only to make a global parameter, because I'm using this data to create the size of the UICollectionViewCell. I think I need to use a delegate method for the UICollectionViewCell to ask the UICollectionViewLayout for the calculated size of cell.
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if (standardUserDefaults) {
[standardUserDefaults setObject:[NSNumber numberWithFloat:itemWidth] forKey:#"cellWidth"];
[standardUserDefaults setObject:[NSNumber numberWithFloat:itemHeight] forKey:#"cellHeight"];
[standardUserDefaults synchronize];
}
// spacing for x
self.lineSpacing = (self.pageSize.width - (self.itemsInOneRow * self.itemSize.width)) / (self.itemsInOneRow + 1);
// spacing for y
self.interitemSpacing = (self.pageSize.height - (self.itemsInOneCol * self.itemSize.height)) / (self.itemsInOneCol + 1);
}
-(int)pagesInSection:(NSInteger)section {
NSLog(#"v9layout: numberOfItemsInSection is %ld", (long)[self.collectionView numberOfItemsInSection:section]);
return ([self.collectionView numberOfItemsInSection:section] - 1) / (self.itemsInOneRow * self.itemsInOneCol) + 1;
}
-(CGSize)collectionViewContentSize {
// contentSize.width is equal to pages * pageSize.width
NSInteger sections = 1;
if ([self.collectionView respondsToSelector:#selector(numberOfSections)]) {
sections = [self.collectionView numberOfSections];
}
int pages = 0;
for (int section = 0; section < sections; section++) {
pages = pages + [self pagesInSection:section];
}
return CGSizeMake(pages * self.pageSize.width, self.pageSize.height);
}
-(void)prepareLayout {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self calculateLayoutProperties];
});
[self.frames removeAllObjects];
NSInteger sections = 1;
if ([self.collectionView respondsToSelector:#selector(numberOfSections)]) {
sections = [self.collectionView numberOfSections];
}
int pagesOffset = 0; // Pages that are used by prevoius sections
int itemsInPage = self.itemsInOneRow * self.itemsInOneCol;
for (int section = 0; section < sections; section++) {
NSMutableArray *framesInSection = [NSMutableArray array];
int pagesInSection = [self pagesInSection:section];
int itemsInSection = [self.collectionView numberOfItemsInSection:section];
for (int page = 0; page < pagesInSection; page++) {
int itemsToAddToArray = itemsInSection - framesInSection.count;
int itemsInCurrentPage = itemsInPage;
if (itemsToAddToArray < itemsInPage) { // If there are less cells than expected (typically last page of section), we go only through existing cells.
itemsInCurrentPage = itemsToAddToArray;
}
for (int itemInPage = 0; itemInPage < itemsInCurrentPage; itemInPage++) {
CGFloat originX = (pagesOffset + page) * self.pageSize.width + self.lineSpacing + (itemInPage % self.itemsInOneRow) * (self.itemSize.width + self.lineSpacing);
CGFloat originY = self.interitemSpacing + (itemInPage / self.itemsInOneRow) * (self.itemSize.height + self.interitemSpacing);
CGRect itemFrame = CGRectMake(originX, originY, self.itemSize.width, self.itemSize.height);
[framesInSection addObject:NSStringFromCGRect(itemFrame)];
}
}
[self.frames addObject:framesInSection];
pagesOffset += pagesInSection;
}
}
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray *attributes = [NSMutableArray array];
NSInteger sections = 1;
if ([self.collectionView respondsToSelector:#selector(numberOfSections)]) {
sections = [self.collectionView numberOfSections];
}
int pagesOffset = 0;
int itemsInPage = self.itemsInOneRow * self.itemsInOneCol;
for (int section = 0; section < sections; section++) {
int pagesInSection = [self pagesInSection:section];
int itemsInSection = [self.collectionView numberOfItemsInSection:section];
for (int page = 0; page < pagesInSection; page++) {
CGRect pageFrame = CGRectMake((pagesOffset + page) * self.pageSize.width, 0, self.pageSize.width, self.pageSize.height);
if (CGRectIntersectsRect(rect, pageFrame)) {
int startItemIndex = page * itemsInPage;
int itemsInCurrentPage = itemsInPage;
if (itemsInSection - startItemIndex < itemsInPage) {
itemsInCurrentPage = itemsInSection - startItemIndex;
}
for (int itemInPage = 0; itemInPage < itemsInCurrentPage; itemInPage++) {
UICollectionViewLayoutAttributes *itemAttributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:startItemIndex + itemInPage inSection:section]];
if (CGRectIntersectsRect(itemAttributes.frame, rect)) {
[attributes addObject:itemAttributes];
}
}
}
}
pagesOffset += pagesInSection;
}
return attributes;
}
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attributes.frame = CGRectFromString(self.frames[indexPath.section][indexPath.item]);
return attributes;
}
#end
This implement the horizontal paging with order of cells one beside each other and not one underneath each other.
I still want to add a drag and drop ability so that the user can change the order of cells. I poset this question about it.
Hopefully, I'll find answer to this and update this answer.
I am new to Collectionview, but not to iOS. I am using custom Flowlayout for my collectionview. I need to return contentsize based on current number of items returned by CollectionView's data source. Is there anyway to know how many items are there currently in flowlayout's collectionview?
#interface LatestNewsFlowLayout : UICollectionViewFlowLayout
-(id)initWithSize:(CGSize) size;
#end
#implementation LatestNewsFlowLayout
-(id)initWithSize:(CGSize) size {
self = [super init];
if (self) {
self.itemSize = size;
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.sectionInset = UIEdgeInsetsMake(0, 10.0, 0, 0);
self.minimumLineSpacing = 5;
}
return self;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)oldBounds {
return YES;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
for(int i = 1; i < [answer count]; ++i) {
UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i];
UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1];
NSInteger maximumSpacing = 5;
NSInteger origin = CGRectGetMaxX(prevLayoutAttributes.frame);
if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) {
CGRect frame = currentLayoutAttributes.frame;
frame.origin.x = origin + maximumSpacing;
currentLayoutAttributes.frame = frame;
}
}
return answer;
}
-(CGSize) collectionViewContentSize
{
CGSize size;
int numOfItems = ???
size.width = 100 * numOfItems;
size.height = 100;
return size;
}
The UICollectionViewFlowLayout is a subclass of UICollectionViewLayout. It should therefore have access to the UICollectionView which should know how many items there are.
[self.collectionView numberOfItemsInSection:0];
You may need to iterate over the section to get the total number of items if you have more that one section.
You can get the number of sections similarly:
[self.collectionView numberOfSections];
Hope this helps! :)
Swifty solution is next:
let itemsCount = Array(0..<collectionView.numberOfSections)
.map { collectionView.numberOfItems(inSection: $0) }
.reduce(0, +)
Now itemsCount contains total number of collection view items across all sections.
If you make a property/outlet called collectionView, try this:
for (int i = 0; i<[collectionView numberOfSections];i++) {//Iterate through all the sections in collectionView
for (int j = 0; j<[collectionView numberOfItemsInSection:i]; j++) {//Iterate through all the rows in each section
numOfItems++;
}
}
In Swift, you can write the following (provided that you have a data source with sections and items):
let totalItemCount = sections.reduce(0) { result, section -> Int in
return result + section.items.count
}
Just replace section with your data source, and items with the item collection name.
+ (NSInteger)countInCollectionView:(UICollectionView *)collectionView {
NSInteger itemCount = 0;
for( int section = 0; section < [collectionView numberOfSections]; ++section ){
itemCount += [collectionView numberOfItemsInSection:section];
}
return itemCount;
}
I like Vadim's answer because it is swifty and clean, but we're creating an array from a range and looping it twice (map and reduce). I'd suggest a similar solution using directly a range with only one loop (reduce):
let totalItems = (0..<collectionView.numberOfSections)
.reduce(0) { res, cur in
res + collectionView.numberOfItems(inSection: cur)
}
In my case I ended up creating an extension for UICollectionView:
extension UICollectionView {
var totalItems: Int {
(0..<numberOfSections).reduce(0) { res, cur in
res + numberOfItems(inSection: cur)
}
}
}
And then just:
collectionView.totalItems