I am pretty new to iOS development and I stumbled upon several issues for which I couldn't easily find any answers yet:
General Setup: I'm using a ScrollView with PageControl inside a TabBarApplication
Is it possible to have the PageControl within the same area as the content of the pages? For me it always gets hidden by the SrollView's Views, but due to display space being rare I really need it on the same height as the actual content.
I've fooled around in some Sandbox-Project and whenever I first started to implement a button into the View of a ScrollView-Page the Pages of the ScrollView wouldn't show immediately anymore, but only after the first scroll attempt. I'd post some code about that but its basically only autogenerated from IB.
This is a general Question about possibilities again: The main design of the Project should be a TabBarApplication with a NavigationController letting you go deeper into sub-menues like it is pretty common. Now in one of the Tabs there should be the PageControl, in which you can then again go into sub-menues by pushing Views on a NavigationController stack . Is this possible?
Some Code for 2.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
[controllers addObject:[NSNull null]]; // [TaskPageViewController new]];
}
self.viewControllers = controllers;
[controllers release];
// a page is the width of the scroll view
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;
pageControl.numberOfPages = kNumberOfPages;
pageControl.currentPage = 0;
}
- (IBAction)changePage:(id)sender {
int page = pageControl.currentPage;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
// update the scroll view to the appropriate page
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
// Set the boolean used when scrolls originate from the UIPageControl. See scrollViewDidScroll: above.
pageControlUsed = YES;
}
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
// replace the placeholder if necessary
TaskPageViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[TaskPageViewController alloc] init]; //WithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
// add the controller's view to the scroll view
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
// We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
// which a scroll event generated from the user hitting the page control triggers updates from
// the delegate method. We use a boolean to disable the delegate logic when the page control is used.
if (pageControlUsed) {
// do nothing - the scroll was initiated from the page control, not the user dragging
return;
}
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
pageControl.currentPage = page;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
// A possible optimization would be to unload the views+controllers which are no longer visible
}
You can have two view hierarchies for this:
Have the page control inside scrollview with origin fixed at contentOffset property
Have the page control in the superview of scrollView, but at a higher index (i.e. floating above it)
This depends on where you put the code of adding the subviews. Is it in the delegate method of scrollView? viewDidLoad? Somewhere else? Some code might help.
Not sure why you'd need to have a page control when it's a drill-down navigation. Pages are for navigating same level items.
Related
I have a problem with my UIScrollView and I hope some of you can help me in some way.
I explain you ...
At the center of my ViewController I have a ScrollView showing the contents of 3 external UIViewController. Through classic for loops I am able to properly display the contents and so far I have not found any problems ...
The problem arises when I perform scrolling between the view controller (horizontally) or, the content of each view controller moves, it does not remain as it was initially set. Now I can not figure out if the problem depends ScrollView or by content, but I think it is a problem of content viewcontroller because I used the autolayout to set constraints ...
At this point I think it's a problem that arises in the ScrollView when I make scrolling to show the next viewcontroller.
Below I insert the screen shoots to show my situation .. as you can see the contents are moved to the right and do not remain in the X position zero.
Can you help me figure out where I'm wrong? what code and 'wrong ..
ScreenShoot
code used
-(void)initializeViewControllerTour {
/* Inserisce in una NSMutableArray i ViewController inizializzati da utilizzare*/
_a_TourVC = [[UPATour alloc] init];
_a_TourVC = [self.storyboard instantiateViewControllerWithIdentifier:#"A_Tour_App"];
_b_TourVC = [[UPBTour alloc] init];
_b_TourVC = [self.storyboard instantiateViewControllerWithIdentifier:#"B_Tour_App"];
_c_TourVC = [[UPCTour alloc] init];
_c_TourVC = [self.storyboard instantiateViewControllerWithIdentifier:#"C_Tour_App"];
_containerArrayVC = [[NSMutableArray alloc] init];
[_containerArrayVC addObject:_a_TourVC];
[_containerArrayVC addObject:_b_TourVC];
[_containerArrayVC addObject:_c_TourVC];
NSInteger index = 0;
/* Imposta i valori per la ScrollView */
CGSize scrollViewSize = CGSizeMake([_containerArrayVC count] * _scrollView.frame.size.width, _scrollView.frame.size.height);
_scrollView.contentSize = scrollViewSize;
_scrollView.scrollEnabled = YES;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.pagingEnabled = YES;
_scrollView.delegate = self;
/* Imposta il PageControl */
_pageControl.userInteractionEnabled = NO;
_pageControl.numberOfPages = [_containerArrayVC count];
_pageControl.currentPage = index;
/* Crea il ciclo For per i ViewController basandosi sulla MutableArray */
for (_currentViewController in _containerArrayVC) {
[self addChildViewController:_currentViewController];
CGFloat originx = (index) * _scrollView.frame.size.width;
_currentViewController.view.frame = CGRectMake(originx, 0, _scrollView.frame.size.width, _scrollView.frame.size.height);
[_scrollView addSubview:_currentViewController.view];
[_currentViewController didMoveToParentViewController:self];
index ++;
}
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat pageW = _scrollView.frame.size.width;
CGFloat segmentPage = _scrollView.contentOffset.x / pageW;
NSInteger page = lround(segmentPage);
_pageControl.currentPage = page;
}
Any reason for this error "CGAffineTransformInvert"
Should I be worried?
I have a .xib with a view, and 4 webViews located outside of the view but within the same xib. Then in the code I add the webViews as subviews to a scroll view inside the view. Would that cause the problem?
Code is below:
//Called first to initialize this class. Also, initializes the nib file and tab bar name.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"More", #"More");
self.tabBarItem.image = [UIImage imageNamed:#"first"];
}
return self;
}
//Initialize the more tab titles and views
-(void)initViewsandTitles{
MoreTabPages = [NSArray arrayWithObjects:self.aboutWebView,
self.newsUpdateWebView,
self.feedbackWebView,
self.creditsResourceWebView, nil];
titles = [[NSArray alloc] initWithObjects:#"About Locavore",
#"News and Updates",
#"Feedback",
#"Credits and Resources", nil];
}
//Initialize the URLs
-(void)initURLs{
websites = [[NSArray alloc] initWithObjects:#"http://www.getlocavore.com/",
#"http://twitter.com/enjoy_locavore",
#"https://getsatisfaction.com/localdirt/products/localdirt_locavore",
#"http://www.getlocavore.com/about", nil];
}
//Called after the controller's view is loaded into memory.
- (void)viewDidLoad
{
[super viewDidLoad]; //Call the super class init method
[self setupSpinner]; //Start the spinner animatio
[self initViewsandTitles]; //Initialize the views and titles
[self initURLs]; //Initialize the URLs
[self setScrollandPageViewProperties]; //Set the scroll and page view properties
[self setUpPageViews]; //Create the web pages
}
//UIScrollViewDelegate Protocol Reference. Called whn the user scrolls the content within the reciever
- (void)scrollViewDidScroll:(UIScrollView *)sender {
if (!pageControlBeingUsed) {
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = self.MoreTabScrollView.frame.size.width;
int page = floor((self.MoreTabScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.MoreTabPageControl.currentPage = page;
self.MoreTabTitle.text = [titles objectAtIndex:page];
}
}
//UIScrollViewDelegate Protocol Reference. Called when the scroll view is about to start scolling content
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
pageControlBeingUsed = NO;
}
//UIScrollViewDelegate Protocol Reference. Called when the scroll view has ended decelerating the scrolling movement
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSLog(#"DID END SCROLLING");
pageControlBeingUsed = NO;
}
//Called when the page control value changes
- (IBAction)MoreTabChangePage {
// Update the scroll view to the appropriate page
CGRect frame;
frame.origin.x = self.MoreTabScrollView.frame.size.width * self.MoreTabPageControl.currentPage;
frame.origin.y = 0;
frame.size = self.MoreTabScrollView.frame.size;
[self.MoreTabScrollView scrollRectToVisible:frame animated:YES];
self.MoreTabTitle.text = [titles objectAtIndex:self.MoreTabPageControl.currentPage];
// Keep track of when scrolls happen in response to the page control
// value changing. If we don't do this, a noticeable "flashing" occurs
// as the the scroll delegate will temporarily switch back the page
// number.
pageControlBeingUsed=YES;
}
//Create a frame for each page and add the page to the scroll view
-(void)setUpPageViews{
//Set up all page views for the more tab
for (int i = 0; i < MoreTabPages.count; i++) {
//Get the current table view controller page
UIWebView *webController= [MoreTabPages objectAtIndex:i];
//Request the URL and load the request
NSURL *urll =[NSURL URLWithString:[websites objectAtIndex:i]];
//Run requests in seperate thread
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
NSURLRequest *firstReq = [NSURLRequest requestWithURL:urll];
[webController loadRequest:firstReq];
dispatch_sync(dispatch_get_main_queue(), ^{
//Create a frame for the current table view controller
CGRect frame = webController.frame;
frame.origin.x = self.MoreTabScrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.MoreTabScrollView.frame.size;
webController.frame = frame;
//Add the the current table view controller page to the scroll view
[self.MoreTabScrollView addSubview:webController];
//Release the controller object it is no longer needed
[webController release];
if(i == 3){
[spinner stopAnimating];
}
});
});
}
}
//Set al the properties for the scroll view and page controll
-(void)setScrollandPageViewProperties{
self.MoreTabScrollView.contentSize = CGSizeMake(self.MoreTabScrollView.frame.size.width * MoreTabPages.count,
self.MoreTabScrollView.frame.size.height);
self.MoreTabScrollView.scrollsToTop = NO;
self.MoreTabScrollView.contentOffset = CGPointMake(self.MoreTabScrollView.frame.size.width, 0);
self.MoreTabPageControl.numberOfPages = MoreTabPages.count;
}
-(void)setupSpinner{
spinner.hidesWhenStopped = YES;
[spinner startAnimating];
}
//Called if the application receives a memory warning
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//Called when the UIViewController's reference count goes to zero
- (void)dealloc {
[super dealloc];
[MoreTabPageControl release];
[MoreTabScrollView release];
[MoreTabTitle release];
[MoreTabPages release];
[titles release];
[websites release];
[spinner release];
}
#end
try setting the minimum zoom scale for your each webview.
[self.aboutWebView.scrollView setMinimumZoomScale:0.1]
it will throw the same error if the scrollview reaches zero at zero zoom.
This may happen with affine transformations when you're scaling UIScrollView instance to 0 either using setZoomScale:animated: method or zoomScale property, so please check your scroll views.
Make sure your zoomScale, minimumZoomScale and maximumZoomScale to set to at least 0.1.
Related:
Calculating minimumZoomScale of a UIScrollView
UIScrollView not respecting minimumZoomScale after changing the subview
I'm creating a "how-to"view where I show the user 5-6 pictures on how to use the app. I want it to be like a container inside the real view. Also I want it to have a transition with swipe and a page control. Something like the AppStore has on the pictures with screenshots of an app if you know what I mean?
Is there an easy way to do this? All help highly appreciated!
here a simple code, but you can customize it with loop, animation or what you want to do ;) ...
- (void)viewDidLoad
{
[super viewDidLoad];
//init scollview
scrollView = [[UIScrollView alloc] initWithFrame:myBounds];
scrollView.delegate = self;
scrollView.pagingEnabled = YES;
//Ajout des covers classiques
for (int i = 0; i < [myCovers count]; i++) {
CGRect frame;
frame.origin.x = scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = scrollView.frame.size;
//Vue 1
UIView *subview1 = [[UIView alloc] initWithFrame:frame];
[subview1 addSubview:[myCovers objectAtIndex:i]];
[scrollView addSubview:subview1];
}
//Content Size Scrollview
scrollViewBack.contentSize = CGSizeMake(scrollViewBack.frame.size.width * ([myCovers count]), scrollViewBack.frame.size.height);
[self.view addSubview:scrollViewBack];
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*([myCovers count]), scrollView.frame.size.height);
[self.view addSubview:scrollView];
//Page Control
pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, scrollView.frame.size.height - PAGECONTROL_HEIGTH - myBaseline, scrollView.frame.size.width, PAGECONTROL_HEIGTH)];
pageControl.numberOfPages = [myCovers count];
[self.view addSubview:pageControl];
}
#pragma mark -
#pragma mark Params setting
- (void) setObjects:(NSArray *)covers {
myCovers = [[NSArray alloc] initWithArray:covers];
}
#pragma mark -
#pragma mark Scrollview delegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender {
CGFloat pageWidth = scrollView.frame.size.width;
NSInteger offsetLooping = 1;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + offsetLooping;
pageControl.currentPage = page % [myCovers count];
}
You can create UIImageView for the 'n' number of images you have.
Add this images on UIScrollView. Keep the size of your scrollview same as your UIImageView size.
Most important thing, enable the Paging property of UIScrollView
It should give you the look same as AppStore screen
shot screen.
I have a problem with memory management. A have a scrollview and every page in it loads from array of view controllers.
I load scrolview page calling the folowing method:
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
BancaTableViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller=[[BancaTableViewController alloc] initWithPageNumber:page];
controller.banks=banks;
[controllersetDelegate:self];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
// add the controller's view to the scroll view
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
This is my unload view controller method which unloads all controllers except the controller of curent page but it doesn't seem to work because the memory keep increasing.
- (void)unloadScrollViewWithPage:(int)page {
for (unsigned i = 0; i < kNumberOfPages; i++) {
if(i!=page){
[viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
}
}
}
How to write the unloadviewcontroller correctly?
You're adding a hard pointer to scrollView, but you never remove the object. (i.e. you keep adding subViews to scrollView and never remove them.
[scrollView addSubview:controller.view];
Try this...
Add a tag to your controller view when you add them to the scrollView, then remove it before you load a new controller. Check syntax - from memory - not tested
if ((NSNull *)controller == [NSNull null]) {
controller=[[BancaTableViewController alloc] initWithPageNumber:page];
controller.banks=banks;
controller.tag = 3;
[controllersetDelegate:self];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
// add the controller's view to the scroll view
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView removeFromView:[scrollView viewWithTag:3]];
[scrollView addSubview:controller.view];
}
I'm trying to implement a Page Control to show some pages with the following storyboard:
As you can see, I've the main view with a scroll view (ViewController3) and another view (PageViewController) that represents the one that I want to push on my scroll view.
On viewDidLoad of ViewController3 I call the following method:
- (void)loadScrollViewWithPage:(int)page
{
if (page < 0)
return;
if (page >= NUMBER_OF_PAGES)
return;
PageViewController *currentViewController = [viewControllers objectAtIndex:page];
if ((NSNull *)currentViewController == [NSNull null])
{
currentViewController = [[PageViewController alloc] init];
currentViewController.pageNumber = [NSString stringWithFormat:#"%d", page];
[viewControllers replaceObjectAtIndex:page withObject:currentViewController];
}
// add the controller's view to the scroll view
if (currentViewController.view.superview == nil)
{
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
currentViewController.view.frame = frame;
currentViewController.view.backgroundColor = [UIColor yellowColor];
[self.scrollView addSubview:currentViewController.view];
self.scrollView.backgroundColor = [UIColor redColor];
NSDictionary *numberItem = [contentList objectAtIndex:page];
currentViewController.numberImage.image = [UIImage imageNamed:[numberItem valueForKey:IMAGE_KEY]];
currentViewController.numberTitle.text = [numberItem valueForKey:NAME_KEY];
}
}
If I run the project, views with yellow background are displayed with no labels, nor image views.
As you can see, I set this background color programmatically. From my understanding, this means that the new view is correctly added to ViewController3, but it is not linked to the one configured on the storyboard.
Do you have any suggestions on how to solve this issue?
Thanks in advance,
yassa
You have to instantiate PageViewController instance using [UIStoryboard instantiateViewControllerWithIdentifier:(NSString *)identifier].
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIStoryboard_Class/Reference/Reference.html