i need help, i am trying to build a app in which i have a viewcontroller with a uiwebview and a navbar with 2 buttons on it. what i want to do is that when user scroll uiwebview navbar automatically hides like slid up sort of. but is not working they way i want it to work. let me post code here.
In viewdidload i put this.
[webPage.scrollView setDelegate:self];
and then i have this method
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
if(scrollView.contentOffset.y == 0) {
//show
NSLog(#"Show");
[self.navigationController setNavigationBarHidden:NO animated:YES];
} else {
NSLog(#"Hide");
[self.navigationController setNavigationBarHidden:YES animated:YES];
//hide
}
}
it NSLog correctly but nothing else navbar still stays. :(
Easy as add this on the ViewController implementation file (.m):
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView.scrollView.delegate = self;
}
#pragma mark - UIScrollViewDelegate Methods
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
self.lastOffsetY = scrollView.contentOffset.y;
}
- (void) scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
bool hide = (scrollView.contentOffset.y > self.lastOffsetY);
[[self navigationController] setNavigationBarHidden:hide animated:YES];
}
AND don't forget to add UIScrollViewDelegate protocol in the header file (.h) :
#interface MyViewController : UIViewController <UIScrollViewDelegate>
...
#end
You can try this:
1. Declare the navigation bar, a constant with your navigation bar height and 2 BOOL variables:
UINavigationBar *navBar;
static const CGFloat kNavBarHeight = 60.0f;
BOOL webViewScrollIsDragging;
BOOL webViewScrollIsDecelerating;
2. In viewDidLoad write the following:
[webView.scrollView setContentInset:UIEdgeInsetsMake(kNavBarHeight, 0, 0, 0)];
[webView.scrollView setScrollIndicatorInsets:UIEdgeInsetsMake(kNavBarHeight, 0, 0, 0)];
[webView.scrollView setContentOffset:CGPointMake(0, -kNavBarHeight) animated:NO];
webView.scrollView.delegate = self;
then initialize and add your UINavigationBar as a subview to self.view at origin (Also make sure that your UIWebView has the same origin, i.e (0,0)).
3. Implement UIScrollViewDelegate methods (Don't forget to add UIScrollViewDelegate protocol):
#pragma mark - UIScrollViewDelegate Methods
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == webview.scrollView)
{
if (scrollView.contentOffset.y == 1 && !webViewScrollIsDragging && !webViewScrollIsDecelerating)
{
[UIView animateWithDuration:0.3
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^(void) {
CGRect navBarFrame = CGRectMake(0,-scrollView.contentOffset.y-kNavBarHeight, self.view.bounds.size.width, kNavBarHeight);
navBar.frame = navBarFrame;
}
completion:nil];
}
else
{
CGRect navBarFrame = CGRectMake(0,-scrollView.contentOffset.y-kNavBarHeight, self.view.bounds.size.width, kNavBarHeight);
navBar.frame = navBarFrame;
}
if (scrollView.contentOffset.y < -kNavBarHeight)
{
[webview.scrollView setScrollIndicatorInsets:UIEdgeInsetsMake(fabsf(scrollView.contentOffset.y), 0, 0, 0)];
}
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (scrollView == webview.scrollView)
{
webViewScrollIsDragging = YES;
}
}
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (scrollView == webview.scrollView)
{
webViewScrollIsDragging = NO;
}
}
- (void) scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
if (scrollView == webview.scrollView)
{
webViewScrollIsDecelerating = YES;
}
}
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
if (scrollView == webview.scrollView)
{
webViewScrollIsDecelerating = NO;
}
}
In iOS8.0 and later
just use one line code:
self.navigationController.hidesBarsOnSwipe = YES;
/// When the user swipes, the navigation controller's navigationBar & toolbar will be hidden (on a swipe up) or shown (on a swipe down). The toolbar only participates if it has items.
// #property (nonatomic, readwrite, assign) BOOL hidesBarsOnSwipe NS_AVAILABLE_IOS(8_0) __TVOS_PROHIBITED;
When sliding down will hide the navigation bar (and become a transparent state bar), when the slide will be displayed on the navigation bar, the process with animation
Related
I'm in the process of making a tutorial, and I'm trying to emulate the style of Path's tutorial like so:
http://www.appcoda.com/wp-content/uploads/2013/06/UIPageViewController-Tutorial-Screen.jpg
My issue is that if set the delegate method as so:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
// The number of items reflected in the page indicator.
return 5;
}
Then I get this stupid black bar under the dots:
http://i.stack.imgur.com/pUEdh.png
Is there a way to make this bar translucent in a way thats similar to setting a UINavigationBar to translucent?
It is very easy to make it work. You just only have to make the pageviewcontroller taller, and place a PageControl into the XIB file.
The trick is put the PageControl in the foreground (and all the other common controls) at the beginning, and update the content of the PageControl with the PageViewController. Here is the code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
// We need to cover all the control by making the frame taller (+ 37)
[[self.pageController view] setFrame:CGRectMake(0, 0, [[self view] bounds].size.width, [[self view] bounds].size.height + 37)];
TutorialPageViewController *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];
// Bring the common controls to the foreground (they were hidden since the frame is taller)
[self.view bringSubviewToFront:self.pcDots];
[self.view bringSubviewToFront:self.btnSkip];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = [(TutorialPageViewController *)viewController index];
[self.pcDots setCurrentPage:index];
if (index == 0) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [(TutorialPageViewController *)viewController index];
[self.pcDots setCurrentPage:index];
index++;
if (index == 3) {
return nil;
}
return [self viewControllerAtIndex:index];
}
- (TutorialPageViewController *)viewControllerAtIndex:(NSUInteger)index {
TutorialPageViewController *childViewController = [[TutorialPageViewController alloc] initWithNibName:#"TutorialPageViewController" bundle:nil];
childViewController.index = index;
return childViewController;
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
// The number of items reflected in the page indicator.
NSInteger tutorialSteps = 3;
[self.pcDots setNumberOfPages:tutorialSteps];
return tutorialSteps;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
// The selected item reflected in the page indicator.
return 0;
}
The same effect can be achieved simply by subclassing UIPageViewController and overriding viewDidLayoutSubviews as follows:
-(void)viewDidLayoutSubviews {
UIView* v = self.view;
NSArray* subviews = v.subviews;
// Confirm that the view has the exact expected structure.
// If you add any custom subviews, you will want to remove this check.
if( [subviews count] == 2 ) {
UIScrollView* sv = nil;
UIPageControl* pc = nil;
for( UIView* t in subviews ) {
if( [t isKindOfClass:[UIScrollView class]] ) {
sv = (UIScrollView*)t;
} else if( [t isKindOfClass:[UIPageControl class]] ) {
pc = (UIPageControl*)t;
}
}
if( sv != nil && pc != nil ) {
// expand scroll view to fit entire view
sv.frame = v.bounds;
// put page control in front
[v bringSubviewToFront:pc];
}
}
[super viewDidLayoutSubviews];
}
Then there is no need to maintain a separate UIPageControl and such.
Swift 3 snippet
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let scrollView = view.subviews.filter({ $0 is UIScrollView }).first,
let pageControl = view.subviews.filter({ $0 is UIPageControl }).first {
scrollView.frame = view.bounds
view.bringSubview(toFront:pageControl)
}
}
Here's a conversion of Zerotool's solution into Swift 2.1, though there's probably a more elegant way to write it:
override func viewDidLayoutSubviews() {
var scrollView: UIScrollView?
var pageControl: UIPageControl?
// If you add any custom subviews, you will want to remove this check.
if (self.view.subviews.count == 2) {
for view in self.view.subviews {
if (view.isKindOfClass(UIScrollView)) {
scrollView = view as? UIScrollView
} else if (view.isKindOfClass(UIPageControl)) {
pageControl = view as? UIPageControl
}
}
}
if let scrollView = scrollView {
if let pageControl = pageControl {
scrollView.frame = self.view.bounds
self.view.bringSubviewToFront(pageControl)
}
}
super.viewDidLayoutSubviews()
}
I don't think you can change the behavior of UIPageViewController, so it seems likely that the Path app uses its own view controller. You can do the same: create your own container view controller that uses a UIPageControl to indicate the current page.
You can simply adjust the alpha of the UIPageViewController's UIPageControl.
First, you should retrieve it from the UIPageViewController like so:
- (UIPageControl *)getPageControlForPageViewController:(UIPageViewController *)pageViewController {
for (UIView *subview in self.pageViewController.view.subviews) {
if ([subview isKindOfClass:[UIPageControl class]]) {
return (UIPageControl *) subview;
}
}
return nil;
}
Next, make use of the function. I've made a property on my ViewController called childPageControl. Give it the UIPageViewController's UIPageControl:
self.childPageControl = [self getPageControlForPageViewController:self.pageViewController];
Next, you can adjust the alpha to give a translucent effect:
self.childPageControl.alpha = .5;
You're very limited in what you can do to affect the UIPageViewController's UIPageControl, but you can at least achieve this with little effort.
Small hack I found today..
Please see the code below.
self.pageController.dataSource = self;
CGRect rect = [self.view bounds];
rect.size.height+=37;
[[self.pageController view] setFrame:rect];
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];
}
}
UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(0, -30, 320, 40)];
[tempview addSubview:thisControl];
thisControl.pageIndicatorTintColor = [UIColor lightGrayColor];
thisControl.currentPageIndicatorTintColor = [UIColor greenColor];
[self.view addSubview:tempview];
this code is in Swift
Add following in your UIPageViewController
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in self.view.subviews {
if view.isKindOfClass(UIScrollView) {
view.frame = UIScreen.mainScreen().bounds
} else if view.isKindOfClass(UIPageControl) {
view.backgroundColor = UIColor.clearColor()
}
}
}
I wanted to do a similar effect in the app I was working on - I used a UIPageViewController with a separate UIPageControl.
This lets you place the UIPageControl anywhere you'd like in the view, including over the top of the UIPageViewController, and you keep its active page dot up to date via the UIPageViewController delegate method:
- (void)pageViewController:(UIPageViewController *)pageViewController
didFinishAnimating:(BOOL)finished
previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers
transitionCompleted:(BOOL)completed {
if (completed) {
self.pageControl.currentPage = [self.pageViewControllers indexOfObject:pageViewController.viewControllers.firstObject];
}
}
No need to traverse the subview hierarchy trying to find the internal UIPageViewController page control, nor having to resize the contents of the internal scrollview.
Hope this helps.
I solve using this code:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.namesImage = #[#"page1.png", #"page2.png", #"page3.png", #"page4.png"];
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
TutorialContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
[[UIPageControl appearance] setPageIndicatorTintColor:[UIColor grayColor]];
[[UIPageControl appearance] setCurrentPageIndicatorTintColor:[UIColor whiteColor]];
[[UIPageControl appearance] setBackgroundColor: [[UIColor blackColor] colorWithAlphaComponent:0.1f]];
[[UIPageControl appearance] setOpaque:YES];
}
Swift 5.2
you can use this code for your requirment
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let myScrollView = view.subviews.filter({ $0 is UIScrollView }).first,
let myPageControl = view.subviews.filter({ $0 is UIPageControl }).first {
myScrollView.frame = view.bounds
view.bringSubviewToFront(myPageControl)
}
}
I found an other workarround that fits me better.
I reuse the code given by zerotool to get the UIPageControl (var called pageControl) and the UIScrollView (var called pageView) used by the UIPageViewController.
Once that done in the viewDidLoad, I just prevent clip subview of pageView and let the content spread more to be beneath the UIPageControl.
The pageControl is beneath the pageView so we have to manually make it come in front.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
if(
[[[self view] subviews] count] == 2
)
{
UIScrollView* pageView = nil;
UIPageControl* pageControl = nil;
UIView* selfView = self.view;
NSArray* subviews = selfView.subviews;
for( NSInteger i = 0 ; i < subviews.count && ( pageView == nil || pageControl == nil ) ; i++ )
{
UIView* t = subviews[i];
if( [t isKindOfClass:[UIScrollView class]] )
{
pageView = (UIScrollView*)t;
}
else if( [t isKindOfClass:[UIPageControl class]] )
{
pageControl = (UIPageControl*)t;
}
}
if( pageView != nil && pageControl != nil )
{
[pageView setClipsToBounds:NO];
[selfView bringSubviewToFront:pageControl];
}
}
}
Once I get my pageView covering the space occupied by the pageControl but under the pageControl, I just have to adjust the nib file use for each viewController displayed as page :
base view should not clip
the first and only subview :
should have constraint to set bottom to -37 (or more if you need but 37 is the size of the pageControl) from bottom of superview
should clip content
I have a question. I want to display the user some content in a UIScrollView. I want to autoscroll the UIScrollView fast from bottom to top (like in the apple stores iPad). I tried to use DDAutoscrollview (If someone knows), but it doesn't work for me. Do have someone a solution for me to autoscroll a UIScrollView? Any code snippets would be nice.
.h
#interface Interface1 : UIViewController {
IBOutlet UIScrollView *scroller;
IBOutlet UILabel *warnung;
}
#property (nonatomic, retain) IBOutlet UIScrollView* scrollView;
.m
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
CGPoint bottomOffset = CGPointMake(self.scrollView.contentOffset.x,
self.scrollView.contentSize.height -
self.scrollView.bounds.size.height);
[self.scrollView setContentOffset:bottomOffset animated:NO];
CGPoint newOffset = self.scrollView.contentOffset;
newOffset.y = 0;
[self.scrollView setContentOffset:newOffset animated:YES];
}
- (void)viewDidLoad {
[scroller setScrollEnabled:YES];
[scroller setContentSize:CGSizeMake(320, 420)];
[super viewDidLoad];
}
Thanks.
> THUMBS UP FOR THE AWNSER THAT WAS GIVEN BY TOBI!!!
Just use setContentOffset:animated:
UIScrollView *scrollView = ...;
CGPoint newOffset = scrollView.contentOffset;
newOffset.y = 0;
[scrollView setContentOffset:newOffset animated:YES];
Edit:
To use it like some kind of start animation you could do this in the scrollView's view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// ...
CGPoint bottomOffset = CGPointMake(self.scrollView.contentOffset.x, self.scrollView.contentSize.height - self.scrollView.bounds.size.height);
[self.scrollView setContentOffset:bottomOffset animated:NO];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGPoint newOffset = self.scrollView.contentOffset;
newOffset.y = 0;
[self.scrollView setContentOffset:newOffset animated:YES];
}
Edit 2 / 3:
To make the scrolling happen slower, use this:
- (void)viewDidLoad
{
[super viewDidLoad];
// ...
CGPoint bottomOffset = CGPointMake(self.scrollView.contentOffset.x, self.scrollView.contentSize.height - self.scrollView.bounds.size.height);
[self.scrollView setContentOffset:bottomOffset animated:NO];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
float scrollDuration = 4.0;
[UIView animateWithDuration:scrollDuration animations:^{
self.scrollView.contentOffset = CGPointMake(self.scrollView.contentOffset.x, 0);
}];
}
In my application I have to add a search bar at the head of the tableview. I am able to add the searchbar but problem is without adding default search bar of ios can i add my customize search bar?? I am giving an image to see what types of search bar will be there...
you can subclass the UISearchBar and override the layoutSubviews method :
- (void)layoutSubviews {
UITextField *searchField;
NSUInteger numViews = [self.subviews count];
for(int i = 0; i < numViews; i++) {
if([[self.subviews objectAtIndex:i] isKindOfClass:[UITextField class]]) { //conform?
searchField = [self.subviews objectAtIndex:i];
}
}
if(!(searchField == nil)) {
searchField.textColor = [UIColor whiteColor];
[searchField setBackground: [UIImage imageNamed:#"yourImage.png"] ];
[searchField setBorderStyle:UITextBorderStyleNone];
}
[super layoutSubviews];
}
Also you can :
//to clear searchbar backgraound
- (void) clearSearchBarBg
{
for (UIView *subview in theSearchBar.subviews)
{
if ([subview isKindOfClass:NSClassFromString(#"UISearchBarBackground")])
{
[subview removeFromSuperview];
break;
}
}
}
//display showSearchButtonInitially in a keyboard
- (void)showSearchButtonInitially
{
UIView * subview;
NSArray * subviews = [theSearchBar subviews];
for(subview in subviews)
{
if( [subview isKindOfClass:[UITextField class]] )
{
NSLog(#"setEnablesReturnKeyAutomatically");
[((UITextField*)subview) setEnablesReturnKeyAutomatically:NO];
((UITextField*)subview).delegate=self;
[((UITextField*)subview) setEnabled:TRUE];
((UITextField*)subview).borderStyle = UITextBorderStyleNone;
break;
}
}
}
Look for Apple DOC for UISearchBar
You have bunch of methods there to get whatever you want
You can get UITextView Inside the search bar by
UITextField *textField = [searchBar.subviews objectAtIndex:2];
if ([textField isKindOfClass:[UITextField class]]) {
//Do your customization
}
Again look for AppleDoc for UITextField. You have bunch of methods for that also.
Yeah definitely. You can make your custom search bar (which is a sub-class of UIView) and add it as subview to the tableHeaderView.
[[searchBarDesign.subviews objectAtIndex:0] removeFromSuperview];
here searchBarDesign is my searchBar name.
I think it's better just set all properties of UISearchBar when it is loaded.
#interface MySearchBar : UISearchBar
#end
#implementation MySearchBar
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self myInitialize];
}
return self;
}
-(void)awakeFromNib
{
[super awakeFromNib];
[self myInitialize];
}
-(void)myInitialize
{
self.backgroundImage = [UIImage imageNamed:#"image.png"];
for (UIView* subview in self.subviews) {
if ([subview isKindOfClass:[UITextField class]]) {
//customize text field
UITextField* textfield = (UITextField*) subview;
}
}
}
#end
I have this code, modified version of Apple's PageScrollView sample. Here the differnce is that m using ViewControllers instead of UIView.
MyClass.h
#interface MyClass : UIViewController {
UIScrollView *scrollView;
UIPageControl *pageControl;
NSMutableArray *viewControllers;
BOOL pageControlUsed;
}
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
#property (nonatomic, retain) IBOutlet UIPageControl *pageControl;
#property (nonatomic, retain) NSMutableArray *viewControllers;
- (IBAction)changePage:(id)sender;
#end
MyClass.m
#import "MyClass.h"
#import "MyViewController.h"
#import "MyViewControllerZero.h"
#import "MyViewControllerOne.h"
#import "MyViewControllerTwo.h"
static NSUInteger kNumberOfPages = 3;
#interface MyClass (PrivateMethods)
- (void)loadScrollViewWithPage:(int)page;
- (void)scrollViewDidScroll:(UIScrollView *)sender;
#end
#implementation MyClass
#synthesize scrollView, pageControl, viewControllers;
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
[controllers addObject:[NSNull null]];
}
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;
// pages are created on demand
// load the visible page
// load the page on either side to avoid flashes when the user starts scrolling
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];
}
- (void)loadScrollViewWithPage:(int)page {
// if (page < 0) return;
// if (page >= kNumberOfPages) return;
if(page==0)
{
MyViewControllerZero *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewControllerZero alloc] init];
[viewControllers replaceObjectAtIndex:page withObject:controller];
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];
}
}
}
if(page==1)
{
MyViewControllerOne *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewControllerOne alloc] init];
[viewControllers replaceObjectAtIndex:page withObject:controller];
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];
}
}
}
if(page==2)
{
MyViewControllerTwo *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{ controller = [[MyViewControllerTwo alloc] init];
[viewControllers replaceObjectAtIndex:page withObject:controller];
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 {
if (pageControlUsed) {
return;
}
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
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
pageControlUsed = NO;
}
// At the end of scroll animation, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
pageControlUsed = NO;
}
- (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)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[viewControllers release];
[scrollView release];
[pageControl release];
[super dealloc];
}
#end
The Above code works absolutely fine in the potrait mode. But when i change the orientation,the whole paging gets screwed up.. :((
please help me to resolve this problem..
Apple didn't design UIViewController to work at any point in a view hierarchy. The views that you're adding to your UIScrollView are managed by view controllers, but I think you'll find that the view controllers for those views don't receive rotation events.
You could try to manually forward all events from the scroll view's controller to the child view controllers, but I think you'll find that to be tedious and error prone. I'd suggest instead that you simply use a single view controller for the scroll view and its child views.
I use a UISearchBar for entering an address to establish a network connection. While the connection is made I want to show the activity indicator instead of the tiny BookmarkButton on the right side of the searchbar. As far as I can see there is no public declared property that would give me access to the correct subview of the searchbar. I have seen this been done, any thoughts?
How about replacing the search icon on the left side with an activity indicator while searches or connections are in progress?
SearchBarWithActivity.h:
#import <UIKit/UIKit.h>
#interface SearchBarWithActivity : UISearchBar
- (void)startActivity; // increments startCount and shows activity indicator
- (void)finishActivity; // decrements startCount and hides activity indicator if 0
#end
SearchBarWithActivity.m:
#import "SearchBarWithActivity.h"
#interface SearchBarWithActivity()
#property(nonatomic) UIActivityIndicatorView *activityIndicatorView;
#property(nonatomic) int startCount;
#end
#implementation SearchBarWithActivity
- (void)layoutSubviews {
UITextField *searchField = nil;
for(UIView* view in self.subviews){
if([view isKindOfClass:[UITextField class]]){
searchField= (UITextField *)view;
break;
}
}
if(searchField) {
if (!self.activityIndicatorView) {
UIActivityIndicatorView *taiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
taiv.center = CGPointMake(searchField.leftView.bounds.origin.x + searchField.leftView.bounds.size.width/2,
searchField.leftView.bounds.origin.y + searchField.leftView.bounds.size.height/2);
taiv.hidesWhenStopped = YES;
taiv.backgroundColor = [UIColor whiteColor];
self.activityIndicatorView = taiv;
[taiv release];
_startCount = 0;
[searchField.leftView addSubview:self.activityIndicatorView];
}
}
[super layoutSubviews];
}
- (void)startActivity {
self.startCount = self.startCount + 1;
}
- (void)finishActivity {
self.startCount = self.startCount - 1;
}
- (void)setStartCount:(int)startCount {
_startCount = startCount;
if (_startCount > 0)
[self.activityIndicatorView startAnimating];
else {
[self.activityIndicatorView stopAnimating];
}
}
#end
I updated the answer from #JohnLemberger to work with iOS 7 (note: I've only tested this on iOS 7), as well as a summary of my changes:
NOTE: this is not very robust code to begin with, since Apple can change the view hierarchy of UISearchBar in any release (as they did between iOS 6 and 7).
SearchBarWithActivity.h (nothing changed):
#interface SearchBarWithActivity : UISearchBar
- (void)startActivity; // increments startCount and shows activity indicator
- (void)finishActivity; // decrements startCount and hides activity indicator if 0
#end
#interface XXTreatmentHeaderViewController : XXViewController
#property (nonatomic, strong, readonly) SearchBarWithActivity *searchBar;
#end
SearchBarWithActivity.m:
1) Show/hide the "magnifying glass" icon when the activity indicator appears
2) Add depth in the view hierarchy search for the UITextField
#interface SearchBarWithActivity()
#property(nonatomic) UIActivityIndicatorView *activityIndicatorView;
#property(nonatomic) int startCount;
#end
#implementation SearchBarWithActivity
- (void)layoutSubviews {
UITextField *searchField = nil;
for(UIView* view in self.subviews){
// on iOS 6, the UITextField is one-level deep
if ([view isKindOfClass:[UITextField class]]){
searchField= (UITextField *)view;
break;
}
// on iOS 7, the UITextField is two-levels deep
for (UIView *secondLevelSubview in view.subviews) {
if([secondLevelSubview isKindOfClass:[UITextField class]]){
searchField= (UITextField *)secondLevelSubview;
break;
}
}
}
if(searchField) {
if (!self.activityIndicatorView) {
UIActivityIndicatorView *taiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
taiv.center = CGPointMake(searchField.leftView.bounds.origin.x + searchField.leftView.bounds.size.width/2,
searchField.leftView.bounds.origin.y + searchField.leftView.bounds.size.height/2);
taiv.hidesWhenStopped = YES;
self.activityIndicatorView = taiv;
_startCount = 0;
[searchField.leftView addSubview:self.activityIndicatorView];
}
}
[super layoutSubviews];
}
- (void)startActivity {
self.startCount = self.startCount + 1;
}
- (void)finishActivity {
self.startCount = self.startCount - 1;
}
- (void)setStartCount:(int)startCount {
_startCount = startCount;
if (_startCount > 0) {
[self.activityIndicatorView startAnimating];
// Remove the "magnifying glass icon"
[self setImage:[UIImage new] forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
} else {
[self.activityIndicatorView stopAnimating];
// Restore the "magnifying glass icon"
[self setImage:nil forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
}
}
#end
I have implemented a category for UISearchBar that shows a UIActivityIndicatorView, depending on state of a AFNetworking's request operation or session task https://gist.github.com/nguyenhuy/a11d15c11200477b05a6.
Just for the record:
for(UIView* view in self.subviews){
if([view isKindOfClass:[UITextField class]]){
searchField=view;
break;
}
}
if(searchField !=)) {
searchField.leftView = myCustomView;
}
You can subclass UISearchBar and call this code in the layoutSubview method. calling this code in layoutSubview makes sure that resize animations work properly.
I update jonsibley's answer by adding support for the cases where a UISearchBar is embedded in a UINavigationBar using the displaysSearchBarInNavigationBar flag.
SearchBarWithActivity.h (added a new property):
#interface SearchBarWithActivity : UISearchBar
- (void)startActivity; // increments startCount and shows activity indicator
- (void)finishActivity; // decrements startCount and hides activity indicator if 0
#property (nonatomic,assign) UINavigationItem *navigationItem;
#end
SearchBarWithActivity.m (get the searchField from the navigationItem if not nil):
#import "SearchBarWithActivity.h"
#interface SearchBarWithActivity()
#property(nonatomic) UIActivityIndicatorView *activityIndicatorView;
#property(nonatomic) int startCount;
#end
#implementation SearchBarWithActivity
#synthesize navigationItem;
- (void)layoutSubviews {
UITextField *searchField = nil;
if(self.navigationItem) {
searchField = (UITextField *)[self.navigationItem titleView];
} else {
for(UIView* view in self.subviews){
// on iOS 6, the UITextField is one-level deep
if ([view isKindOfClass:[UITextField class]]){
searchField= (UITextField *)view;
break;
}
// on iOS 7, the UITextField is two-levels deep
for (UIView *secondLevelSubview in view.subviews) {
if([secondLevelSubview isKindOfClass:[UITextField class]]){
searchField= (UITextField *)secondLevelSubview;
break;
}
}
}
}
if(searchField) {
if (!self.activityIndicatorView) {
UIActivityIndicatorView *taiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
taiv.center = CGPointMake(searchField.leftView.bounds.origin.x + searchField.leftView.bounds.size.width/2,
searchField.leftView.bounds.origin.y + searchField.leftView.bounds.size.height/2);
taiv.hidesWhenStopped = YES;
self.activityIndicatorView = taiv;
_startCount = 0;
[searchField.leftView addSubview:self.activityIndicatorView];
}
}
[super layoutSubviews];
}
- (void)startActivity {
self.startCount = self.startCount + 1;
}
- (void)finishActivity {
self.startCount = self.startCount - 1;
}
- (void)setStartCount:(int)startCount {
_startCount = startCount;
if (_startCount > 0) {
[self.activityIndicatorView startAnimating];
// Remove the "magnifying glass icon"
[self setImage:[UIImage new] forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
} else {
[self.activityIndicatorView stopAnimating];
// Restore the "magnifying glass icon"
[self setImage:nil forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
}
}
#end
In your ViewController:
#import "SearchBarWithActivity.h"
- (void)viewDidLoad
{
[super viewDidLoad];
// Embed the search bar into NavigationBar and setup the navigation item in order to show the spinner
[self.searchDisplayController setDisplaysSearchBarInNavigationBar:YES];
[(SearchBarWithActivity *)self.searchDisplayController.searchBar setNavigationItem:self.navigationItem];
}
I hope this saves somebody's time.
Since it seems like the depth of the UITextField keeps changing I figured I would add a recursive solution.
-(NSArray * ) findAllSubviewsForView:(UIView * ) view{
NSMutableArray * views = [[NSMutableArray alloc] init];
for(UIView * subview in view.subviews){
[views addObjectsFromArray:[self findAllSubviewsForView:subview]];
}
[views addObject:view];
return views;
}
You can use this array to find the UITextField,
UITextField * searchField = nil;
for(UIView * view in [self findAllSubviewsForView:self]){
if([view isKindOfClass:[UITextField class]]){
searchField = (UITextField *) view;
}
}