I'm a junior and I have been really struggling to get this to work, would really appreciate some help.
I am trying to build an App where you can scroll through different WebViews by selecting an item from a table in a popover (while the popover remains in place). Unfortunately, I can't get my scrollView to move when I select an item from the popover view.
I have set up a UIScrollView with page control in a main UIViewController. The scrollView is populated with (pageViewController) UIWebViews. The main UIViewController has a nav bar with a button, when the button is clicked it creates a Popover View. When I select an item from the table in the popover view I want my UIScrollView to scroll to a certain position (equates to a selected page), but the scrollView does not move.
The method below takes the value selected from the popover and uses the page number to determine where the scrollView should scroll to, but it doesn't work because at this point my scrollView is Null.
I can supply more code if needed.
main UIViewController.m code:
- (void)setDetailItem:(id)newDetailItem {
if (detailItem != newDetailItem) {
[detailItem release];
detailItem = [newDetailItem retain];
NSString *pgnum = [[NSString alloc] initWithString:[detailItem description]];
int pg;
if(pgnum == #"Page 1"){
pg =1;
}
if(pgnum == #"Page 2"){
pg =2;
}
if(pgnum == #"Page 3"){
pg =3;
}
[pgnum release];
pageControl.currentPage = pg;
[self loadScrollViewWithPage:pg - 1];
[self loadScrollViewWithPage:pg];
[self loadScrollViewWithPage:pg + 1];
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pg;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
pageControlUsed = YES;
}
if (popoverController != nil) {
[popoverController dismissPopoverAnimated:YES];
}
}
This is how I triggered the popOverController
The infoButton is on the navbar.
The scrollView is NOT NULL in the infoButton
- (void)infoButton:(id)sender {
NSLog(#"Entering: infoButton");
// [self inspectView:self.view level:#""];
if (self.popoverController == nil) {
PopoverViewController *popoverViewController =
[[PopoverViewController alloc]
initWithNibName:#"PopoverViewController"
bundle:[NSBundle mainBundle]];
popoverViewController.navigationItem.title = #"Navigation";
UINavigationController *navController =
[[UINavigationController alloc]
initWithRootViewController:popoverViewController];
UIPopoverController *popover =
[[UIPopoverController alloc]
initWithContentViewController:navController];
popover.delegate = self;
[popoverViewController release];
[navController release];
self.popoverController = popover;
[popover release];
//NSLog(#"User pressed the info button");
}
[self.popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; }
UIPopover, being neither the most obvious class to use memory-management wise, nor the most stable, does not require you to release it before you present it. In fact, doing so causes it to throw a fatal exception! Popovers should be released after they are removed from the screen, and/or in -dealloc.
Related
I have implemented a niceUIPageViewControl with aPageControl. When swiping the indicator changes and shows the correct current page.
However i noticed that all that it takes for the current page indicator to switch is to start swiping. Meaning if i start swiping but then let go of the finger the current page indicator has switched like the page has been switched however it has not. This is the code that i am using to make the switch:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
NSUInteger index = [self.navigationController.viewControllers indexOfObject:viewController];
self.pageControl.currentPage = index;
}
Another thing i noticed was that when i swipe right and left changing views FAST the page indicator is just stuck and does not move.
So it only works when you are not changing views fast. If you need any additional code let me know. Thank you.
Edit
This is the code i use to implement my UIPageViewController.
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the data model
self.navigationItem.title = #"Tabell";
self.identifiers = [[NSMutableArray alloc] init];
[self.identifiers addObject:#"rankTable"];
[self.identifiers addObject:#"page2"];
// Create page view controller
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
self.pageViewController.delegate = self;
self.navigationController.delegate = self;
CGSize navBarSize = self.navigationController.navigationBar.bounds.size;
CGPoint origin = CGPointMake( navBarSize.width/2, navBarSize.height/2 );
self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(origin.x, origin.y+16,
0, 0)]; //Here added 45 to Y and it did the trick
self.pageControl.pageIndicatorTintColor = navbarColor;
self.pageControl.currentPageIndicatorTintColor = [UIColor lightGrayColor];
[self.pageControl setNumberOfPages:2];
[self.navigationController.navigationBar addSubview:self.pageControl];
UITableViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
CGFloat tabBarHeight = self.tabBarController.tabBar.frame.size.height;
// Change the size of page view controller
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - tabBarHeight);
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
Why don't you implement the UIPageViewControllerDelegate's
- pageViewController: didFinishAnimating: previousViewControllers: transitionCompleted: method? It has a transitionCompleted boolean which tells you if you should update your page control. Your current implementation seems to be buggy because as soon as you star swiping a page, it loads a view controller for the next page and calls the ...didShow... method. Hope this hepls.
I am using UIPageViewController to show images full screen, the UIViewController which is added to UIPageController as a sub view / child has the images being showed using ImageView.
Problem is the images arent comming fullscreen, instead the pagecontrol view's donts are appearing at the bottom and that space is completely wasted. Please check the image attached.
Here is the code
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
NewsItemViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
Here NewsItemViewController is UIViewController showing images and some text and The MainViewController implements UIPageViewControllerDataSource protocol and necessary methods in MainViewController.
I believe there has to be a way to do show the things in full screen.
*** Also the MainViewController is a part of a storyboard if that matters.
Finally got the solution myself I just hide the page control from UIViewPageController and then extended the size of the UIPageViewController to cover up the gap left due to absense of page control.
NSArray *subviews = self.pageController.view.subviews;
UIPageControl *thisControl = nil;
for (int i=0; i<[subviews count]; i++) {
if ([[subviews objectAtIndex:i] isKindOfClass:[UIPageControl class]]) {
thisControl = (UIPageControl *)[subviews objectAtIndex:i];
}
}
thisControl.hidden = true;
self.pageController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height+40);
Curious. The docs say:
If both of the methods in “Supporting a Page Indicator” are
implemented and the page view controller’s transition style is
UIPageViewControllerTransitionStyleScroll, a page indicator is
visible.
Those two methods are:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
}
Are you implementing these two data source methods? If so, perhaps if you remove them you won't have to manually remove the page control (dots)? A quick test would be to change the
UIPageViewControllerTransitionStyleScroll
to
UIPageViewControllerTransitionStylePageCurl
and see if the page control indicator dots go away. (After commenting out your hide method, of course.)
UIPageViewController dots are only shown when you have implemented following method:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController;
check in you code. and also returning back zero in this method will hide the dots (UIPageControl)
I am adding for swift 2.2 compatible code
for view in self.view.subviews {
if view.isKindOfClass(UIScrollView) {
view.frame = UIScreen.mainScreen().bounds
} else if view.isKindOfClass(UIPageControl) {
view.backgroundColor = UIColor.clearColor()
}
}
This is the Swift 4 compatible solution, embedded in viewDidLayoutSubviews
for view in self.view.subviews {
if view.isKind(of:UIScrollView.self) {
view.frame = UIScreen.main.bounds
} else if view.isKind(of:UIPageControl.self) {
view.backgroundColor = UIColor.clear
}
}
Swift version :). Return Zero for below implementation inside UIPageViewController subclass.
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int { return 0 }
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int { return 0 }
Set your pager controller as so
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
And implement,this method should return any value greater than 1
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
// The selected item reflected in the page indicator.
return 2;
}
Now the gap at the bottom space is removed and no page control shown :)
Hello i have a nib file which contains 3 UIButtons , i need to change the attributes of button at runtime after loading from nib but i don't know what am i doing wrong. I have also NSlog after changing the booleans but i always get '0' in output. Any help thx
-(void)SettingBtnPressed:(id)sender
{
UIButton *btn =(UIButton*)sender;
UITableViewCell *btncell =(UITableViewCell *)[btn superview];
if ([[recentActivities objectAtIndex:btn.tag] isKindOfClass:[MeetingSummary class]]) {
[MainManager getSharedInstance].Summary = [recentActivities objectAtIndex:btn.tag];
MeetingSummary *ms=[recentActivities objectAtIndex:btn.tag];
Meeting *m=ms.meeting;
if(![popoverController isPopoverVisible]){
lastPoint = sender;
if (![m.endedDate isEqualToString:#"1/1/0001"]) {
if (settingpop) {
[settingpop release];
}
settingpop = [[SettingsPopViewController alloc] initWithNibName:#"SettingsPopViewController" bundle:nil];
settingpop.start.hidden=YES;
settingpop.start.enabled=NO;
NSLog(#"%i",settingpop.start.hidden);
NSLog(#"%i",settingpop.start.enabled);
settingpop.btnShowDetail.enabled=NO;
settingpop.btnShowDetail.hidden=YES;
// [settingpop.start removeFromSuperview];
// [settingpop.btnShowDetail removeFromSuperview];
settingpop.view.frame=CGRectMake(0, 0, 250, 54);
}
else
{
settingpop = [[SettingsPopViewController alloc] initWithNibName:#"SettingsPopViewController" bundle:nil];
}
settingpop.delegate = self;
[settingpop setActivityView:self.view];
popoverController = [[[UIPopoverController alloc] initWithContentViewController:settingpop] retain];
[popoverController setPopoverContentSize:CGSizeMake(250.0f, 162.0f)];
[popoverController presentPopoverFromRect:btn.frame inView:btncell
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}else{
[popoverController dismissPopoverAnimated:YES];
}
}
}
Move the row:
settingpop.view.frame=CGRectMake(0, 0, 250, 54);
before the row:
settingpop.start.hidden=YES;
Are you talking about adjusting the properties of the start and btnShowDetail controls of SettingsPopViewController? You cannot access its controls immediately after the initWithNibName. You have to wait until after the view has been created, i.e., wait until after viewDidLoad is called, because the NIB may well not have been loaded until then.
I've got a standard UINavigation controller and push my screens with push and pop as normal. However, i have one screen that switches between two view controllers on a button press so the screen flips over top reveal the other viewcontroller and visa versa. You can switch between them at will.
Now, I want the back button to work as normal so I swap the top view controller to achieve this as follows:
-(IBAction)switchToSlowProductEntry:(id)sender
{
NSLog(#"Switching to slow product entry");
// Replace the top view with the findProductView
FindProductView *findProdView = [ProductViewInstances shared].findProductView;
NSMutableArray *views = [self.navigationController.viewControllers mutableCopy];
[views removeLastObject];
[views addObject:findProdView];
// if sender is nil then assume we started from the viewDidLoad so no animation
if(sender)
{
[UIView transitionWithView:self.navigationController.view duration:0.3 options:UIViewAnimationOptionTransitionFlipFromRight animations:^
{
[self.navigationController setViewControllers:views animated:NO];
}
completion:^(BOOL finished) {}];
}
else
[self.navigationController setViewControllers:views animated:NO];
NSLog(#"Views: %#", views);
[views release];
[ProductViewInstances shared].lastScreen = SlowProductEntryView;
}
-(IBAction)switchToQuickProductEntry:(id)sender
{
NSLog(#"Switching to fast product entry");
// Replace the top view with the findProductView
QuickOrderEntryView *quickProductView = [ProductViewInstances shared].quickProductView;
NSMutableArray *views = [self.navigationController.viewControllers mutableCopy];
[views removeLastObject];
[views addObject:quickProductView];
if(sender)
{
[UIView transitionWithView:self.navigationController.view duration:0.3 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^
{
[self.navigationController setViewControllers:views animated:NO];
}
completion:^(BOOL finished) {}];
}
else
[self.navigationController setViewControllers:views animated:NO];
NSLog(#"Views: %#", views);
[views release];
[ProductViewInstances shared].lastScreen = QuickProductEntryView;
}
I have a similar piece of code for the other screen. I'm using the ProductViewInstances class to maintain the two view controllers as I do not want the classes to get unloaded as I'm maintaining stage on the screen.
When you want to move forward from these screens, I do the push as normal to a new screen. It work and I go back after reviewing the products I added. If I press back I get back to the above screen and everything seems normal. However, when I press my custom back button (I need to do processing if back pressed) I run into a problem.
The popViewController does nothing. Here is the code in the base class to manage the custom back button.
-(void) viewDidLoad
{
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Back", nil)
style:UIBarButtonItemStyleBordered
target:self
action:#selector(myCustomBack)] autorelease];
if(![ProductViewInstances shared].findProductView)
{
[ProductViewInstances shared].findProductView = [[FindProductView alloc] init];
[ProductViewInstances shared].findProductView.customer = self.customer;
}
if(![ProductViewInstances shared].quickProductView)
{
[ProductViewInstances shared].quickProductView = [[QuickOrderEntryView alloc] init];
[ProductViewInstances shared].quickProductView.customer = self.customer;
}
}
-(void) goBack
{
if([[ProductViewInstances shared].quickProductView checkIfItemsPending])
{
// Pop up dialog
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Save Entries", nil)
message:NSLocalizedString(#"Your entries will be lost", nil)
delegate:self
cancelButtonTitle:NSLocalizedString(#"Cancel", nil)
otherButtonTitles:NSLocalizedString(#"Save", nil), nil];
[alert show];
[alert release];
}
else
{
// Remove rows from quick item entry screen
[[ProductViewInstances shared].quickProductView removeRowsFromtable];
if(didPressHome)
[self popToSpringBoard:YES];
else
[self.navigationController popViewControllerAnimated:YES];
}
}
So when I press back I have to check if the entries will be lost. The pop to SpringBoard pops back a couple of screens and basically calls the following:
NSArray *controllers = appDelegate.navigationController.viewControllers;
UIViewController *springboard = [controllers objectAtIndex:2];
[appDelegate.navigationController popToViewController:springboard animated:animated];
However, the popViewController animated call does nothing... Like as if it never happened.
Donie
#selector(myCustomBack) will not call goBack unless you left out some code.
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