There is the question: I set a custom view named ZHLockView. I put it in the storyboard and I set the constraints. But I can't get the correct width in - (id)initWithCoder:(NSCoder *)aDecoder, it is always 320, what should I do to get the correct width?
Last I know in the - (void)layoutSubviews, I can get the right width, but it will going twice. Is there a better idea?
See, you're getting width 320 because your view is of that size on storyboard.
If the width of the view is equal to superview, which is indeed again equal to width of device, you can use following code to get width of view at anytime and dont need to worry about where to write.
Swift :-
CGFloat width = CGRectGetWidth(UIScreen.mainScreen.bounds);
CGFloat height = CGRectGetHeight(UIScreen.mainScreen.bounds);
Objective-C :-
CGFloat width = CGRectGetWidth([[UIScreen mainScreen] bounds]);
CGFloat height = CGRectGetHeight([[UIScreen mainScreen] bounds]);
Hope you understood and it helps you.. :)
the code is here
objc
#define KScreenWidth [UIScreen mainScreen].bounds.size.width
CGFloat height = 0;
CGFloat margin = (KScreenWidth - columnCount * btnW) / (columnCount + 1);
for (int i = 0; i < btnCount; i++) {
int row = i / columnCount;
int column = i % columnCount;
CGFloat btnX = margin + column * (btnW + margin);
CGFloat btnY = row * (btnW + margin);
height = btnH + btnY;
ZHButtonView *button = [[ZHButtonView alloc] initWithFrame:CGRectMake(btnX, btnY, btnW, btnH)];
button.userInteractionEnabled = NO;
button.currentIndex = i;
[button setBackgroundImage:[UIImage imageNamed:#"gesture_node_normal"] forState:UIControlStateNormal];
[button setBackgroundImage:[UIImage imageNamed:#"gesture_node_highlighted"] forState:UIControlStateSelected];
[self addSubview:button];
}
The view will be load with the value in the xib at the first start, next when the autolayout engine makes the first pass, it will be set to a new value according to the constraints set.
Why would you need to know the size? Autolayout is made to do not care about it.
-layoutSubviews, -viewWillLayout, -viewDidLayout are called when the layout engine needs a update, that could means Different times before displaying your view.
Related
So far I have customized the tableview and implemented the iPad General Setting Page. Below is the code for tableview which will change frame accordingly. But the issue is when I insert/delete rows or section in the tableview. My tableviewcell backgroundview (not cell) width get shrinks. Any idea what wrong am I doing here?
- (void)setFrame:(CGRect)frame
{
if (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad)
{
CGFloat inset =10;
frame.origin.x += inset;
frame.size.width -= 2 * inset;//The issue is in this line
}
[super setFrame:frame];
}
I found the simple solution to achieve this. No need of customizing UITableView subclass. Just take the outlet of tableview and set the frame and change the color of backgroundview. like that below:-
CGFloat tableBorderLeft = 10;
CGFloat tableBorderRight = 10;
CGRect tableRect = self.view.frame;
tableRect.origin.x += tableBorderLeft; // make the table begin a few pixels right from its origin
tableRect.size.width -= tableBorderLeft + tableBorderRight; // reduce the width of the table
yourTableView.frame = tableRect;
self.view.backgroundColor=[UIColor colorWithRed:(239/255.0f) green:(239/255.0f) blue:(244/255.0f) alpha:1.0];
My app is based on a tabBarController and has 2 tabs.
I load data for the 2nd tab in the background while the app launches at tab 1, so users don't need to wait when they click tab 2.
My problem is: it's laggy to switch to tab 2 if I load the data in advance and there is much data to show in cells of the tableView belonging to tab 2. If there are not many cells to show, then it's not laggy at all.
I guess it's because generating cells is time-consuming, so the view is blocked when there are too many cells. How do I optimize this?
Important! This code has not been tested in the XCode, but contains some parts from real projects.
The CellView class could be created either as a Nib or even manually using calculateCellHeight method from ServicesHelper.m. In both cases the layoutSubviews method must be implemented where the resize detailTextLabel UILabel code to be placed. The CellView labels Font and Color must be the same as used in calculateCellHeight method.
ServicesHelper.m
#define FontRegular(fontSize) [UIFont fontWithName:#"HelveticaNeue" size:fontSize]
// the height of fixed part of the cell, fixed height UILabel + some padding
#define kFixedPartCellHeight 20
#define kLeftPaddingWidth 20
#define kLandscapeHeightKey #"landscapeKey"
#define kPortraitHeight #"portraitKey"
+ (NSDictionary *) calculateCellHeight: (NSString *) text {
// dynamic height label
UILabel *detailTextLabel = [UILabel new];
UIFont *mainFont = FontRegular(14.0);
// specifying font and colour to be used inside cell is important to get precise frame rect
[detailTextLabel setFont: mainFont];
[detailTextLabel setTextColor:[UIColor blackColor]];
detailTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
detailTextLabel.numberOfLines = 0;
[textLabel setBackgroundColor: [UIColor clearColor]];
[detailTextLabel setBackgroundColor: [UIColor clearColor]];
// get the width of the cell for both orientations
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat landscapeWidth = MAX (screenRect.size.width, screenRect.size.height);
CGFloat portraitWidth = MIN (screenRect.size.width, screenRect.size.height);
// kLeftPaddingWidth - is just a white space left and right to the UILabel inside cell
// we set the UILabel width with maximum possible height, then set text and shrink it using sizeToFit to get the exact size
detailTextLabel.frame = CGRectMake (0, 0 , landscapeWidth - kLeftPaddingWidth * 2, CGFLOAT_MAX);
textLabel.frame = detailTextLabel.frame;
detailTextLabel.text = text;
[detailTextLabel sizeToFit];
CGFloat landscapeHeight = detailTextLabel.frame.size.height + kFixedPartCellHeight;
detailTextLabel.frame = CGRectMake (0, 0 , portraitWidth - kLeftPaddingWidth * 2, CGFLOAT_MAX);
textLabel.frame = detailTextLabel.frame;
detailTextLabel.text = text;
[detailTextLabel sizeToFit];
CGFloat portraitHeight = detailTextLabel.frame.size.height + kFixedPartCellHeight;
return #{kLandscapeHeightKey: landscapeHeight, kPortraitHeightKey: portraitHeight};
}
TableView.h
#property (nonatomic, strong) NSMutableArray *arrayOfTexts;
#property (nonatomic, strong) NSMutableDictionary *dictOfHeights;
TableView.m
- (void) precalculateHeight {
if (nil == self.dictOfHeights) self.dictOfHeights = [NSMutableDictionary new];
CGRect screenRect = [[UIScreen mainScreen] bounds];
// height of two screens
CGFloat maxHeight = MAX (screenRect.size.width, screenRect.size.height) * 2;
CGFloat totalHeight = 0;
CGFloat portraitHeight;
CGFloat landscapeHeight;
NSDictionary *dictHeights;
for (int i = 0; i < self.arrayOfTexts.count; i++ ) {
dictHeights = [ServicesHelper calculateCellHeight: arrayOfTexts[i]];
portraitHeight = [dictHeights[kPortraitHeightKey] floatValue];
[self.dictOfHeights setValue: dictHeights forKey:#(i)];
totalHeight += portraitHeight;
if (totalHeight > maxHeight) break;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.dictOfHeights && self.dictOfHeights[#(indexPath.row)]) {
return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ?
[self.dictOfHeights[#(indexPath.row)][kPortraitHeightKey] floatValue] :
[self.dictOfHeights[#(indexPath.row)][kLandscapeHeightKey] floatValue];
} else {
// you decide, call calculateCellHeight for particular row, right from here, or also calculate rows height for the coming set of cells
// #todo:
}
return CGFLOAT_MIN;
}
I am putting few icons on UITableViewCell. The number of icons can vary from 1 to 6. Icons should not have fixed X position and instead they should be dynamically placed considering the cell space. So if there are 3 icons they should look placed centric to the cell. Also, the icon spacing should also vary. For instance when it is 6 icons all of them will be placed with less margins in between and when there are 3 then margin will be more and so will be the X position.
Please suggest some quick way to calculate this frame. I am running the app both on iOS 6 and iOS 7.
This is what I have tried by far but this does not seems to work well with icon count variation. Also, in between space is not dynamic with this.
int maxIconTypes = 6;
CGFloat innerPadding = ([self isIOS7]) ? 15.0f : 9.0f;
CGRect adjustedBoundsIOS6 = [self bounds];
CGFloat adjustedWidth = ([self isIOS7]) ? self.bounds.size.width : adjustedBoundsIOS6.size.width;
CGFloat xOrigin = innerPadding;
CGFloat iconViewSize = 25.0f;
CGFloat ySpacer = (self.bounds.size.height - iconSize) / 2;
CGFloat xSpacer = ((adjustedWidth - (innerPadding * 2)) - (maxRequestTypes * iconViewSize)) / (maxIconTypes - 1);
for (NSString *icon in iconList) {
UIImageView *anIconView = [[UIImageView alloc] initWithImage:[UIImage icon]];
if (anIconView.image) {
anIconView.frame = CGRectMake(xOrigin + originPadding, ySpacer, anIconView.image.size.width, anIconView.image.size.height);
[self.contentView addSubview:anIconView];
xOrigin += anIconView.frame.size.width + xSpacer;
}
}
However many items you have, the number of spaces is the same if you want equal spacing with half spacing at the start and end:
half width full width half
half width full width full width half
So, you just need to know the full width available and the combined width of the items. A simple multiply (to get the combined width of the items) and subtract (from the full width available) gives you the remaining width for the 'spacers'. Divide by the number of items to get the xSpacer, set the initial xOrigin to xSpacer * 0.5
Here is an example of what Wain is explaining:
//Im doing this in a UIView rather than in a UITableViewCell but idea is the same
int cellWidth = CGRectGetWidth(self.view.bounds);
//num of icons
int iconCount = 6;
//size of icon
int iconSize = 25;
//The max padding we can have
float maxPadding = (cellWidth - (iconSize * iconCount)) / iconCount;
//Our offset
float xOrigin = maxPadding / 2;
//Loooop
for (int i = 0; i < iconCount; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(xOrigin, self.view.center.y, iconSize, iconSize)];
xOrigin += (iconSize + maxPadding);
label.backgroundColor = [UIColor blueColor];
[self.view addSubview:label];
}
I'm writing a UI for iOS and I need to create a scrolling view of note-type widgets. I'm experimenting with creating a scrollview and adding some UIView objects to it, but for some reason, I can't scroll, am I doing something wrong? Here's what I'm doing:
[_testScrollView setBackgroundColor:[UIColor purpleColor]];
NSUInteger size = 100;
float height = _testScrollView.frame.size.height/10;
float width = _testScrollView.frame.size.width;
float currTop = 0;
CGSize fooSize = CGSizeMake(_testScrollView.frame.size.width, _testScrollView.frame.size.height);
[_testScrollView setContentSize:fooSize];
for (NSUInteger i = 0; i < size; i++)
{
double red = ((double)arc4random() / ARC4RANDOM_MAX);
double green = ((double)arc4random() / ARC4RANDOM_MAX);
double blue = ((double)arc4random() / ARC4RANDOM_MAX);
CGRect currFrame = CGRectMake(0, currTop, width, height);
UIView* testView = [[UIView alloc] initWithFrame:currFrame];
[testView setBackgroundColor:[UIColor colorWithRed:red green:green blue:blue alpha:1.0]];
[_testScrollView addSubview:testView];
currTop += height;
}
I would've expected to scroll through the scrollview and be able to go through all of the 100 UIViews added. What am I missing?
you need to set the content size for your scroll view to the height of all the uiviews. use
setContentSize:cgSizeMake(_testScrollView.frame.size.width,size*height)
after the for loop
Just print(Log screen) your height of contentsize in scrollview.there you can see the scrollview height...you need to add heights of all subviews to get height of contentsize for scrollview...
Can we change the behavior of UIScrollView that it scrolls RTL content in it's reverse mode.
You can achieve it by rotating UIScrollView in Child views by 180 (Pi).
self.scrollView.transform = CGAffineTransformMakeRotation(M_PI);
subView.transform = CGAffineTransformMakeRotation(M_PI);
count = 6;
[self.scrollView setFrame:CGRectMake(scrollView.frame.origin.x, scrollView.frame.origin.y, 320, 480)];
[pageControl setNumberOfPages:count];
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * count, self.scrollView.frame.size.height);
for(int i=count-1; i>=0; i--) { //start adding content in reverse mode
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * (count - i - 1); //ar
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
[imageView setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", i]]];
[self.scrollView addSubview:imageView];
}
//scroll to the last frame as it's the first page for RTL languages
CGRect frame;
frame.origin.x = scrollView.frame.size.width * (count - 1);
frame.origin.y = 0;
frame.size = scrollView.frame.size;
[self.scrollView scrollRectToVisible:frame animated:NO];
The page control also needs to be indicate the last dot as the first dot (or first page)
- (void)scrollViewDidScroll:(UIScrollView *)sender {
// Update the page when more than 50% of the previous/next page is visible
CGFloat pageWidth = self.scrollView.frame.size.width;
int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = (page % count);
}
I try it for xCode 7 and iOS 9 work good.
[self._imagesScrollView setTransform:CGAffineTransformMakeScale(-1, 1)];
I m not sure if I understand the question correctly but I take it like this: You want to have a (let's say) horizontal UIScrollView where you can add elements from right to left and when the contents exceed the scroll view frame you want to be able to scroll to the left rather than to the right in order to see the last elements.
In this case you can do this. For every new element calculate the total width you need and set it as contentOffset:
[scrollView setContentSize:CGSizeMake(self.childView.frame.size.width,
scrollView.frame.size.height)];
Above I have a childView where the elements are added. It is placed at the right most position inside the scrollView minus 1 element width. Every time an element is added call:
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x + kElementWidth,0.0)];
which will offset everything to the left. Then:
self.videoQueueCollectionView.center = CGPointMake(self.childView.center.x + kElementWidth,
self.childView.center.y);
That will snap eveything back to where it was BUT with the contentOffset set to the left. Which means that you can now scroll to the right.
I use this code for Swift 3
self.scrollView.transform = CGAffineTransform(scaleX:-1,y: 1);
You can achieve this by setting semanticContentAttribute
self.scrollView.semanticContentAttribute = .forceRightToLeft