I have a UISplitView like design (not really UISplitView, I used View container to mimic it).
The left side container has a table view and right side container has a collection view.
If I click the tableview cell in the left container, the right side collection view will be changed accordingly.
The collection view is paginated, it always has two pages.
If I scroll to the second page of the collection view and now I click a new table view cell in the left container, it will load the correct collection view in the right container, but it is in the second page instead of the first page!
I search around and could not find a solution.
I appreciate for any advices.
Update: Added related code.
Following code is in the right container.
- (void)viewDidLoad
{
[super viewDidLoad];
[self setup];
self.collectionView.delegate = self;
self.pageControl.currentPage = 0;
[self.collectionView scrollRectToVisible:CGRectMake(0, 0, self.collectionView.frame.size.width, self.collectionView.frame.size.width) animated:NO];
}
The function setup is defined as following:
-(void)setup
{
self.collectionView.pagingEnabled = YES;
self.collectionView.directionalLockEnabled = YES;
self.collectionView.bounces = NO;
self.collectionView.showsHorizontalScrollIndicator = NO;
}
Thanks.
Why can't you write a code to scroll the scroll view to show the frame of the first page whenever user selects a row in left container.
[contentScrollView scrollRectToVisible:pageRect animated:YES];
Following is the example I have created to solve this issue :
You can download code form Here
After downloading source just add following method to MLKPageViewController.m
- (void)showPageAtIndex:(NSInteger)index
{
if( index >= self.contentVCs.count )
return;
UIView *contentView = ((UIViewController *)[self.contentVCs objectAtIndex:index]).view;
CGRect pageRect;
if( mlkPageControl.currentPage == FIRST_PAGE || mlkPageControl.currentPage == LAST_PAGE )
{
pageRect = CGRectMake( (index * CONTENT_VIEW_SPACING) + index * contentView.frame.size.width, contentScrollView.frame.origin.y , contentScrollView.frame.size.width, contentScrollView.frame.size.height);
}
else
{
pageRect = CGRectMake( (index * CONTENT_VIEW_SPACING) + index * contentView.frame.size.width - CONTENT_VIEW_SPACING, contentScrollView.frame.origin.y , contentScrollView.frame.size.width, contentScrollView.frame.size.height);
}
[contentScrollView scrollRectToVisible:pageRect animated:YES];
}
Call above method from "ViewDidLoad" method of MLKPageViewController by passing page index.
Related
I am new in iOS. I am building a basic project where I have two view controllers.
First one named as tabViewController and the second one as detailViewController.
in tabViewController I have a collectionView. When I am tapping on collection view cell it is pushing to detail view controller. In detail view controller there are three image views. One is just in front of the screen (x=0, y=0, width=320, height=420). The other two imageViews are in the positions at left and right side of the one which is in front of the screen. The two imageViews are in positions as x=-330,y=0,width=320,height=420 and x=330,y=0,width=320,height=420. Consider just about iPhone5.
Now, the problem that is happening is I cant make the scrollView's position to see the image that is in left side of the screen.
The code I have written is
- (void)viewDidLoad {
[super viewDidLoad];
_myScrollView.delegate = self;
_myScrollView.pagingEnabled = YES;
_selectedImageString = [_patternArray objectAtIndex:*(_selectedIndex)];
_strImageNext = [_patternArray objectAtIndex:*(_selectedIndex)+1];
_myScrollView.bounds = CGRectMake(_myImageView2.frame.origin.x, _myScrollView.frame.origin.y, _myScrollView.frame.size.width, _myScrollView.frame.size.height);
if (*(_selectedIndex) == 0) {
_strImagePrevious = nil;
}
else{
_strImagePrevious = [_patternArray objectAtIndex:*(_selectedIndex)-1];
}
self.navigationController.navigationBar.hidden = NO;
_myImageView.image = [UIImage imageNamed:_selectedImageString];
_myImageView.frame = CGRectMake(_myScrollView.frame.origin.x, _myScrollView.frame.origin.y, _myScrollView.frame.size.width, _myScrollView.frame.size.height);
self.myScrollView.backgroundColor = [UIColor yellowColor];
_myImageView3.image = [UIImage imageNamed:_strImageNext];
if (*(_selectedIndex) == 0) {
_myImageView2.image = nil;
}
else {
_myImageView2.image = [UIImage imageNamed:_strImagePrevious];
}
[_myScrollView setScrollEnabled:YES];
[_myScrollView setContentSize:CGSizeMake((_myImageView2.frame.size.width+_myImageView2.frame.size.width+_myImageView2.frame.size.width+_myImageView.frame.size.width+ 30.0), _myScrollView.frame.size.height)];
}
Here is a sample code to move left with contentOffset. Hope it will help you.
your_scroll.contentOffset = CGPointMake(-100.0, 0.0);
For the tutorial for my app, I am trying to create a UILabel that drifts across the displayed screen when a view appears and then is destroyed so that if the user comes back to that view during the tutorial, the UILabel will be created anew and again drift across the page. Here's a sample of one of the custom view controllers I am displaying with my UIPageViewController:
//this is just a custom UILabel with some padding
#property (nonatomic) PaddedLabel *directionsLabel;
//I have tried setting UILabel to nil or removing it from view
-(void)viewWillDisappear:(BOOL)animated
{
NSLog(#"view is disappearing");
//this does not remove the label
self.directionsLabel = nil;
//nor does this
[self.directionsLabel removeFromSuperview];
}
- (void)viewDidAppear:(BOOL)animated
{
[self messageTutorial];
}
- (void)messageTutorial
{
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
PaddedLabel *directionsLabel = [[PaddedLabel alloc] initWithFrame:CGRectMake(_width/2, _height/2, 100, 100)];
directionsLabel.text = #"test";
CGRect f = directionsLabel.frame;
f.size = size;
directionsLabel.frame = f;
directionsLabel.center = self.view.center;
f = directionsLabel.frame;
f = directionsLabel.frame;
f.origin.y = .1*height;
directionsLabel.frame = f;
[self.view addSubview:directionsLabel];
[UIView animateWithDuration:TUTORIAL_DISAPPEAR_TIME animations:^{
directionsLabel.alpha = .5;
CGRect f = directionsLabel.frame;
f.origin.y = height - f.size.height*1.4;
directionsLabel.frame = f;
NSLog(#"animating");
} completion:^(BOOL finished) {
[directionsLabel removeFromSuperview];
//this also doesn't actually remove the label
}];
}
The problem is that if the user pages back to see this view she now sees a new label and the old one, so that if you page back and forth back and forth you end up with many many labels all saying the same thing, in different stages of progressing across the screen.
How can I remove the UILabel when the view disappears and add/create a new one when the view appears/reappears?
Thank you.
The code in your viewWillDisappear method is backwards. You need:
- (void)viewWillDisappear:(BOOL)animated {
NSLog(#"view is disappearing");
[self.directionsLabel removeFromSuperview];
self.directionsLabel = nil;
}
As you had it, setting self.directionsLabel to nil before trying to remove it results in a no-op.
Also, be sure to set self.directionsLabel when you create the label.
Instead of setting your label to nil and effectively destroying the label object (assuming automatic reference counting is on) rather use the following method to hide and show the label as and when your need it.
[self.directionsLabel setHidden:YES]; // hides it
[self.directionsLabel setHidden:NO]; // shows it
You've got the right idea setting objects you're not using to nil and removing them from the super view but it's over kill. A UILabel object uses a negligible amount of memory and you're better off creating the object once and then changing it's properties as you need to.
You don't seem to be setting self.directionsLabel to anything when you create the directionsLabel inside the messageTutorial method. It is a local instance of the label there. You should set it in the method somewhere.
Afterwards, removing it from the superview in viewWillDisappear will work (tested to verify).
I'm having a fight with UICollectionView to adopt it to my needs.
I'm trying to build a collapsing tag cloud. All elements can dynamically change size based on text inside.
When collection is displayed, I would like to display only first row and hide rest of content.
If there is more content than just for one row I want to show a button as a last item in first row - after selecting it, I will change collection size to fit it's content.
First step:
After click:
I was able to achieve desired effect with this code:
- (void) updateFrame
{
if(self.showFull)
{
self.showMoreButton.hidden = YES;
self.bottomConstrain.constant = 0;
self.frame = CGRectMake(0, 0, self.collectionView.frame.size.width, self.collectionView.contentSize.height);
}
else
{
self.showMoreButton.hidden = NO;
self.bottomConstrain.constant = self.originalConstrainValue;
self.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
}
}
But button in my case is placed completely outside the collection:
I'm hitting my head against a brick wall how to make this button to be a part of collection view.
It's the first time I'm playing with UICollectionView and UICollectionViewFlowLayout and it's too complex for me still to understand where would be best spot to put it.
One idea I had was to play a button over UICollectionView and position it at the end of first row - but I don't know how to get first row size from collection. With this approach it will be problematic also to cover last item in a row.
I think the best will be to put this element as a UICollectionViewCell, but I have no idea how to approach that - how to predict where it will placed and how to hide it later.
Any ideas will be highly appreciated.
Current template project is here:
https://www.zipshare.com/download/eyJhcmNoaXZlSWQiOiJjNTg2MGFkNC1mYWYxLTRlMzItOTA1YS1hMWFjOGFkMjMzYjUiLCJlbWFpbCI6Imdya3J1a293c2tpQGdtYWlsLmNvbSJ9
I'd suggest putting this button as a different cell INSIDE the collection view. Then, in collectionView:didSelectItemAtIndexPath: you would check to see if the special item was selected and react accordingly. I would let this button always be the last item in the collection view so you know where it is. When the items have expanded and everything is displayed, make sure you flip your logic and compress the collection view when this special cell is pressed.
You'll need to save some state to do this:
1.The array of tags
whether or not the collection view is expanded or not
Once you have this info, you can just flip the bits and reload the collection view section.
I've managed to achieve what I wanted with this code (thanks for atreat for suggestion).
I'm estimating which element won't fit in first row, and pushing "More" tag into tags array.
There is some logic also to make sure that More button will fit after last element in this row, if not it's pushed instead of this element. Maybe some will find it usefull.
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout;
CGFloat collectionWidth = self.frame.size.width;
CGFloat lastFittingItemRight = 0.0f;
CGFloat spacingBetweenElements = flowLayout.minimumInteritemSpacing;
CGFloat sectionInsetLeft = flowLayout.sectionInset.left;
CGFloat sectionInsetRight = flowLayout.sectionInset.right;
CGFloat rightEdge = sectionInsetLeft;
for (int i = 0; i < [self collectionView:self.collectionView numberOfItemsInSection:0]; i++)
{
CGSize elementSize = [self collectionView:self.collectionView layout:flowLayout sizeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
CGFloat elementWidth = elementSize.width;
rightEdge += elementWidth;
if(rightEdge > collectionWidth - sectionInsetRight)
{
self.moreButtonIndexPath = [NSIndexPath indexPathForItem:i inSection:0];
break;
}
lastFittingItemRight = rightEdge;
rightEdge += spacingBetweenElements;
}
if(self.moreButtonIndexPath)
{
NSString* showMoreText = #"More";
CGSize moreButtonSize = [self sizeForTagWithText:showMoreText];
CGFloat rightEdgeMoreButton = lastFittingItemRight + spacingBetweenElements + moreButtonSize.width;
NSInteger moreButtonIndex = self.moreButtonIndexPath.row;
if(rightEdgeMoreButton > collectionWidth - sectionInsetRight)
{
moreButtonIndex = moreButtonIndex-1;
}
[self.tags insertObject:showMoreText atIndex:moreButtonIndex];
}
I'm retrieving json data from server. The json data includes some images and more text and last but not least json size is not predictable.
Here is how I retrieve json:
-(void)viewWillAppear:(BOOL)animated {
if (self.jsonUrl == nil) {
// This control is about MWPhotoBrowser. If user returned from photo browser do not retrieve data twice
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underRightViewController isKindOfClass:[DetailContextViewController class]]) {
self.slidingViewController.underRightViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailAbout"];
}
self.jsonUrl = [self.exampleAd getAdJSONLink];
NSLog(#"%#", self.jsonUrl); //
[self backgroundTask]; // retrieving data
} else {
// User returned from MWPhotoBrowser so set the current height again/
// This is not working somehow.
[self.scrollView setContentSize:(CGSizeMake(320, currentHeight + 10))];
}
}
I'm adding all those data to an UIScrollView as a subview. Let's say this is B view. So height of scrollview is uncertain. I'm calculating the height and setting like this:
[self.scrollView setContentSize:(CGSizeMake(320, currentHeight + 10))];
That's working well. Here is my problems: If I go back to A from B and again go A to B, content size is not working. Scrolling not working too, but my currentHeight is showing the right value. Even the retrieving method is not working enough, actually there is something like cache.
There is same problem while using MWPhotoBrowser. When I use present modal controller than return to B view again scroll is not working.
What am I doing wrong, any approach or clue would be great.
I have a popover screen, with inside it :
a label, that may or may not appear (title)
a search bar, that may or may not appear
a label, that may or may not appear, and has a variable height (help label)
a scrollview, that may or may not appear, and has a variable height (some infos about the following table)
a table view
In order to present something nice, in viewDidLoad, I move the various frames to place the objects correctly and not have unused spaces cluttering my popover. Besides, I then resize the table (to take the most place needed), and the popover via contentSizeInPopover (to avoid having a near-empty huge popover). All that resizing seems to work nicely, but I have one big problem : with all that resizing done, some cells of my UITableView become unresponsive. One or two cells, usually the second one, only respond if i tap in their outer corners, but the rest of the cell completely ignore any touches.
I've tried everything : moving all to viewWillAppear, letting the autoresize do its job (doesn't seem to work either), but I still have this problem every time. I've found that if I comment the lines involved with changing the frame of the table, or the ones in contentSizeInPopover, the problem stops, but then my view is messed up, so this ins't a fix.
If anyone could give me something to get out of this mess, that would be awesome.
- (CGFloat)getHeightWithoutTable {
return LIST_TITLE_HEIGHT + (self.searchBar.hidden ? 0 : LIST_SEARCH_BAR_HEIGHT) + (self.helpLabel.hidden ? 0 : self.helpLabel.frame.size.height + LIST_STD_SPACE) + (self.errorScrollView.hidden ? 0 : self.errorScrollView.frame.size.height + LIST_STD_SPACE);
}
-(void)viewDidLoad {
[super viewDidLoad];
self.tableViewOutlet.backgroundView = nil;
self.originData = [NSMutableArray array];
self.searchedData = [NSMutableArray array];
if (self.helper != nil) {
CGFloat heightOffset = 0;
// Content
self.originData = [self.helper getData];
self.tableData = [NSMutableArray arrayWithArray:self.originData];
// Title
NSString *title = [self.helper getPopoverTitle];
if (title == nil) {
self.popoverTitle.hidden = YES;
heightOffset -= LIST_TITLE_HEIGHT;
} else {
self.popoverTitle.text = [self.helper getPopoverTitle];
}
// Search
if ([self.originData count] [self getStdHeight] / 3){
self.helpLabel.lineBreakMode = UILineBreakModeTailTruncation;
[self.helpLabel sizeThatFits:CGSizeMake(self.helpLabel.frame.size.width, [self getStdHeight] / 3)];
}
heightOffset += (self.helpLabel.frame.size.height - LIST_HELP_STD_HEIGHT);
}
// Errors
if ([self.helper respondsToSelector:#selector(getErrors)]) {
self.errors = [self.helper getErrors];
}
if (self.errors == nil || [self.errors count] == 0) {
self.errorScrollView.hidden = YES;
self.errorBg.hidden = YES;
heightOffset -= LIST_ERROR_STD_HEIGHT + LIST_STD_SPACE;
} else {
[self createErrorView];
heightOffset += (self.errorScrollView.frame.size.height - LIST_ERROR_STD_HEIGHT);
}
// Table
CGFloat previewHeight = LIST_CELL_HEIGHT * [self.tableData count] + LIST_STD_SPACE;
CGFloat remainingHeight = LIST_MAX_HEIGHT - [self getHeightWithoutTable] - LIST_STD_SPACE;
CGFloat tableHeight = MIN(previewHeight, remainingHeight);
CGRect tableFrame = self.tableViewOutlet.frame;
self.tableViewOutlet.frame = CGRectMake(tableFrame.origin.x, tableFrame.origin.y + heightOffset, LIST_WIDTH, tableHeight);
// Selected items
if ([helper getSelectedObject] != nil){
int index = [self.tableData indexOfObject:[helper getSelectedObject]];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableViewOutlet scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
}
- (CGSize)contentSizeForViewInPopover {
if (self.navigationController) {
return CGSizeMake(LIST_WIDTH, LIST_MAX_HEIGHT);
} else {
CGFloat totalHeight = [self getHeightWithoutTable] + self.tableViewOutlet.frame.size.height + LIST_STD_SPACE;
return CGSizeMake(LIST_WIDTH, totalHeight);
}
}
(gist if you need some coloring to help you)
An image of the nib :
Just a shot in the dark, since you have not provided any code. If you are adding things to the UITableCellView, just remember that a lot of components have their UserInteractionEnabled set to NO, which will disable the ability to interact with it. Make sure that any items you add to the cell that potentially take up the space where you are tapping (presumably the center of the cell?) have their UserInteractionEnabled set to YES.
The reason why the edges might still work is that the UITableCellView consists of 3 main parts, so you are probably only changing the center part.
Post some code then we can have a better look.
Found the answer myself : the fact I was using a self-filled UIScrollView next to my UITableView seemed to be the problem. As soon as I replaced the UIScrollView by a proper UITableView, the problem disappeared.