I need to make UIScrollView with UIViewController's inside, every UIViewController has UITableview.
When I tap to UITableview, all UITableViewCells are disappear.
How I initialise UIViewController inside UIScrollView?
- (void)viewDidLoad {
[super viewDidLoad];
int width = 0;
for (int i = 0; i < [self.dishes count]; i++) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Order" bundle:nil];
OneDayDishViewController *myVC = (OneDayDishViewController *)[storyboard instantiateViewControllerWithIdentifier:#"OneDayDishViewController"];
myVC.dishes = self.dishes[i];
myVC.menuTitle = self.menuTitle;
[self.scrollView addSubview:myVC.view];
width += 375;
}
self.scrollView.contentSize = CGSizeMake(width,0);
}
After looking for in google & stack overflow I found this solution
So I done something like this
in my .h file (where I have UIScrollView):
#property (strong, nonatomic) OneDayDishViewController *myVC;
Than in my .m file:
- (void)viewDidLoad {
[super viewDidLoad];
int width = 0;
for (int i = 0; i < [self.dishes count]; i++) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Order" bundle:nil];
self.myVC = [storyboard instantiateViewControllerWithIdentifier:#"OneDayDishViewController"];
self.myVC.dishes = self.dishes[i];
self.myVC.menuTitle = self.menuTitle;
[self.scrollView addSubview:self.myVC.view];
width += 375;
}
self.scrollView.contentSize = CGSizeMake(width,0);
}
Now I also have N elements in my scrollview, if I tap the last element - it's work fine, if I tap any other element - the problem is the same.
So what I do wrong or how I can create (strong) proper dynamically and is it good practice?
Thanks
UPDT:
Thank you #CZ54
my solution is add childviewcontroller so do
[self addChildViewController:myVC];
[self.scrollView addSubview:myVC.view];
[myVC didMoveToParentViewController:self];
instead of
[self.scrollView addSubview:myVC.view];
Adding the subview is not enough.
You have to add the entire controller using
addChildViewController: ( from https://developer.apple.com/documentation/uikit/uiviewcontroller/1621394-addchildviewcontroller )
And you have to retain every instance you are adding.
Change
#property (strong, nonatomic) OneDayDishViewController *myVC;
into
#property (strong, nonatomic) NSMutableArray<OneDayDishViewController*> *myVC;
Related
Consider the following situation:
I have this myViewcontroller. On the Storyboard I have added a UIViewController, defined it as a myViewcontroller, with a couple of UILabels on them. I have properly connected them to the IBOutlets.
Then, I have a scrollview. I add instances of myViewcontroller to that scrollview as such:
- (void)configureInfoViewController
{
for (int i = 0; i < [Lists count]; i++) {
myViewcontroller *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"myLabelLayout"];
[self addChildViewController:vc];
}
}
- (void)configureScrollView
{
for (int i = 0; i < [self.childViewControllers count]; i++) {
CGRect frame = theScrollView.frame;
...
[[[self.childViewControllers objectAtIndex:i] view] setFrame:frame];
[theScrollView addSubview:[[self.childViewControllers objectAtIndex:i] view]];
}
}
So, I have as many subviews in theScrollview as there are objects in Lists. So far so good.
But now, I want to set the text of a label in one of these subviews. I want to accomplish that by pushing a UIButton on that very same instance of that myViewcontroller.
The question is: how do I set that myLable.title?
I hope this makes sense.
I think the REAL question is, that bothers me:
How do I know on what subview my UIButton resides. I could then send that metadata to other methods. Thanks.
I am assuming you have created outlets for your labels and when you push the button can't you just access the label and change its text in the button's IBAction method?
Here is what you need,
#import "MyViewcontroller.h"
#implementation MyViewcontroller
- (IBAction)myButtonAction:(id)sender {
_myLabel.text = #"My custom value";
}
#end
FYI, your InfoViewController your configureScrollView method should be like this. I don't see your scroll view content size adjusted in your code.
- (void)configureScrollView {
for (int i=0; i<self.childViewControllers.count; i++) {
CGRect frame = theScrollView.frame;
// scrolling vertically here
frame.origin.y = CGRectGetHeight(frame) * i;
UIView *view = [[self.childViewControllers objectAtIndex:i] view];
[view setFrame:frame];
[theScrollView addSubview:view];
}
theScrollView.contentSize = CGSizeMake(CGRectGetWidth(theScrollView.frame), CGRectGetHeight(theScrollView.frame) * self.childViewControllers.count);
}
I have a problematic UIView called as MyListViewController. It is at the first tab in my application.
I am adding a subview in the viewDidLoad section( there are some constraints defined on the subview ). When I run the application the subview doesn't fit to it's root view well. However, if I navigate to an another tab and then re-click to the first tab, the application fits the subview properly.
Also I can't use [self.detailView setTranslatesAutoresizingMaskIntoConstraints:YES]; because it causes heaps of constraint conflicts.
Anyway, I wrote my custom method, patchSubViewPadding, in order to fit the subView to it's rootView which is defined as detailView. As I stated above if I navigate to one of the other tabs and then navigate to the the first tab the application shows up the subview properly.
#interface MyListViewController ()
#property (strong, nonatomic) CustomViewController *customViewController;
#end
#implementation MyListViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
CustomViewController *viewController = (CustomViewController *)[storyboard instantiateViewControllerWithIdentifier:#"customView"];
self.customViewController = viewController;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
[self.detailView addSubview:self.customViewController.view];
}
-(void)viewDidAppear:(BOOL)animated
{
[CommonUtilities patchSubViewPadding:self.detailView subView:self.customViewController.view padding:0];
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[CommonUtilities patchSubViewPadding:self.detailView subView:self.customViewController.view padding:0];
}
#end
And this is my custom method:
+(CGRect)patchSubViewPadding:(UIView *)superView subView:(UIView *)subView padding:(float)padding
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
if (padding == 0)
padding = -4.0f;
CGRect frame = superView.bounds;
frame.origin.y = 0;
frame.size.width = frame.size.width - padding;
subView.frame = frame;
return subView.frame;
}
return subView.frame;
}
I tried the following properties either in viewDidLoad or viewWillLayoutSubviews but it didn't work.
[self.detailView setNeedsLayout];
[self.detailView setNeedsUpdateConstraints];
Any ideas? What am I doing wrong?
Move the method call for patchSubViewPadding: out of viewDidAppear and to the bottom of viewDidLoad.
I did search first, and have seen somewhat similar issues, but no definitive answer on how to solve it. The problem I'm having with my app is that I have 3 navigation controllers that are added as child view controllers into a UIScrollView. I originally wanted to use a UIPageViewController instead, but that turned out to be a mess since you can't disable the bouncing on it. For each navigation controller, a tableView controller is embedded within it in storyboard.
viewDidAppear and viewDidDisappear are not called for my child view controllers because their views are added as subviews to the paging scroll view at runtime, and I kindof believe is why I can't figure out how to solve this.
I need each of my table view controller's to respond to the user touching the status bar for the respective tableView to scroll to the top.
Here is the code I'm using for the setup of the app's main scroll view and adding the 3 view controller's views to it:
I appreciate any help offered!
MainViewController.h
#import <UIKit/UIKit.h>
#interface MainViewController : UIViewController <UIScrollViewDelegate>
#end
MainViewController.m:
#import "MainViewController.h"
#interface MainViewController ()
#property (nonatomic, strong) UINavigationController *settings;
#property (nonatomic, strong) UINavigationController *hehTwo;
#property (nonatomic, strong) UINavigationController *hehOne;
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.settings = [self.storyboard instantiateViewControllerWithIdentifier:#"Settings"];
self.hehTwo = [self.storyboard instantiateViewControllerWithIdentifier:#"HehTwo"];
self.hehOne = [self.storyboard instantiateViewControllerWithIdentifier:#"HehOne"];
[self addChildViewController:self.settings];
[self addChildViewController:self.hehTwo];
[self addChildViewController:self.hehOne];
CGRect hehTwoFrame = self.hehTwo.view.frame;
hehTwoFrame.origin.x = 320;
self.hehTwo.view.frame = hehTwoFrame;
CGRect hehOneFrame = self.hehOne.view.frame;
hehOneFrame.origin.x = 640;
self.hehOne.view.frame = hehOneFrame;
[self.scrollView addSubview:self.settings.view];
[self.scrollView addSubview:self.hehTwo.view];
[self.scrollView addSubview:self.hehOne.view];
// Setting offset so the farthest right controller is heh One
[self.scrollView setContentOffset:CGPointMake(640,0)];
self.scrollView.delegate = self;
self.scrollView.bounces = NO;
self.scrollView.delaysContentTouches = NO;
self.scrollView.contentSize = CGSizeMake(960, self.view.frame.size.height);
self.scrollView.scrollsToTop = NO;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat width = scrollView.frame.size.width;
NSInteger page = (scrollView.contentOffset.x + (0.5f * width)) / width;
if (page == 0)
{
// Settings
}
else if (page == 1)
{
// Heh Two
}
else
{
// Heh One
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
First of all, you need to set self.tableView.scrollsToTop = NO; on HehTwo and HehOne (if these two are initially hidden) in their viewDidLoad implementation.
And now, update this method:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat width = scrollView.frame.size.width;
NSInteger page = (scrollView.contentOffset.x + (0.5f * width)) / width;
if (page == 0)
{
// Settings
((UITableViewController *)self.settings.viewControllers[0]).tableView.scrollsToTop = YES;
((UITableViewController *)self.hehOne.viewControllers[0]).tableView.scrollsToTop = NO;
((UITableViewController *)self.hehTwo.viewControllers[0]).tableView.scrollsToTop = NO;
}
else if (page == 1)
{
// Heh Two
((UITableViewController *)self.settings.viewControllers[0]).tableView.scrollsToTop = NO;
((UITableViewController *)self.hehOne.viewControllers[0]).tableView.scrollsToTop = NO;
((UITableViewController *)self.hehTwo.viewControllers[0]).tableView.scrollsToTop = YES;
}
else
{
// Heh One
((UITableViewController *)self.settings.viewControllers[0]).tableView.scrollsToTop = NO;
((UITableViewController *)self.hehOne.viewControllers[0]).tableView.scrollsToTop = YES;
((UITableViewController *)self.hehTwo.viewControllers[0]).tableView.scrollsToTop = NO;
}
}
That's all! This should fix it.
I am building an app with some scrolling inside. In short, this is my configuration:
1) class_1: a view with a set of elements inside (something similar to camera row) listed by using a collection view
2) class_2: a modal view called by class_1 where I show each singular element selected (from class_1). Inside the same class I have also implemented the horizontal scroll of all the elements
3) class_3: a simple viewcontroller with an imageview inside which will contain the element I will show in class_2
Running my app looks like class_3 is not added as subview of class_2 and all the singular content I try to load are not visible.
Below is my class_2 code
Interface:
#import <UIKit/UIKit.h>
#import "class_3.h"
#interface class_2 : UIViewController
#property (strong, nonatomic) IBOutlet UIPageControl *pageControl;
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#property (weak, nonatomic) NSArray *imageList;
#end
Implementation:
#interface class_2 () {
NSMutableArray *_viewControllers;
}
- (void)loadScrollViewWithImage:(NSUInteger)indexImage;
#end
#implementation class_2
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// _imageList is passed from class_1 when I call class_2. Contains an array of images
self.pageControl.numberOfPages = [_imageList count];
self.pageControl.currentPage = 0;
// I store inside an array N objects as the number f images I have to show. At the beginning the array has only null objects, then it will contains the views based on the images I have scrolled.
//I am storying my images inside a view because maybe I will show more controls and not just the image. It will be much easier manage them.
_viewControllers = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < [_imageList count]; i++) {
[_viewControllers addObject:[NSNull null]];
}
// I set some properties of my scrollview defined as outlet
self.scrollView.frame = CGRectMake(0, 0, 320, 345);
self.scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.scrollView.frame) * [_imageList count], CGRectGetHeight(self.scrollView.frame));
self.scrollView.pagingEnabled = YES;
// this is set to NO, I switched to YES just now for debug (at least I can see how big the area is since there are no images visible)
self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.scrollsToTop = NO;
self.scrollView.delegate = self;
// for testing now I always take the first and second elements of my array. I load both so when I move from the first image to the second is already there and there is no gap/flashing effect
[self loadScrollViewWithImage:0];
[self loadScrollViewWithImage:1];
}
#pragma mark - Private methods
- (void)loadScrollViewWithImage:(NSUInteger)indexImage {
// I scrolled until the last image, from here I can only go back
if (indexImage >= [_imageList count])
return;
// I replace the placeholder (null) object
class_3 *controller = [_viewControllers objectAtIndex:indexImage];
if ((NSNull *)controller == [NSNull null]) {
controller = [[class_3 alloc] init];
[_viewControllers replaceObjectAtIndex:indexImage withObject:controller];
}
// I add the controller's view to the scroll view - in case this is the first time I'm scrolling this image
if (controller.view.superview == nil) {
// I set my controller frame as the scrollview works as container
CGRect frame = self.scrollView.frame;
frame.origin.x = CGRectGetWidth(frame) * indexImage;
frame.origin.y = 0;
controller.view.frame = frame;
// I finally add my viewcontroller as child of my superview and subview of the scrollview
[self addChildViewController:controller];
[self.scrollView addSubview:controller.view];
[controller didMoveToParentViewController:self];
// I set an image corrisponding with the element selected from class_1
controller.imageView.image = [_imageList objectAtIndex:indexImage]
// I also tried with a fake image...just in case. No one of them is visible
//controller.imageView.image = [UIImage imageNamed:#"tempPhoto"];
}
}
My class_3 is a viewcontroller which hosts only an image view shoulb be populated with the corresponding image selezted before.
The pagecontroller is used to show where I scrolling (letf/right) and how many elements there are. The scrolling is phisically managed with the scrollview.
Here is my class_3 definition
Interface:
#import <UIKit/UIKit.h>
#import "class_3.h"
#interface class_3 : UIViewController
#property (strong, nonatomic) IBOutlet UIImageView *imageView;
#end
Implementation (nothing really impressive)
#import "class_3.h"
#interface class_3 ()
#end
#implementation class_3
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.imageView.image = [UIImage imageNamed:#"tempPhoto"];
}
#end
What am I missing?
Tnx
Your class_3 should be a UIView, instead of a UIViewController, subclass. See here
The style should be like iBooks or pictures in foursquare. When we flip to the left we see the next picture being pulled. How to do so? Is there a code to do so? I think there must have been a ready code for that
It sounds like you want UIPageViewController.
Try this. One solution. Revise as needed.
Create a new project of type Single View Application, name it BookEffect, uncheck Use Storyboards, uncheck Include Unit Tests, but be sure to check the option Use Automatic Reference Counting.
Then add to the project a few pictures, for example, in JPEG format.
Create a new file - use the Objective-C Class template with a subclass of UIViewController, select the option With XIB for user interface, and name it SinglePageViewController. Save the file inside the BookEffect folder.
Once added, select the SinglePageViewController.xib and add to the view a UILabel control and UIImageView control and make the appropriate connections to the File's Owner.
Revise the SinglePageViewController.h to look like this:
#import <UIKit/UIKit.h>
#interface SinglePageViewController : UIViewController
{
NSUInteger pageNumber;
}
#property (strong, nonatomic) IBOutlet UILabel *titleLabel;
#property (strong, nonatomic) IBOutlet UIImageView *imageView;
#property (nonatomic, assign) NSUInteger pageNumber;
- (void) setPageNumber: (NSUInteger) newPageNumber;
- (NSUInteger) pageNumber;
#end
In SinglePageViewController.m add:
#synthesize imageView, titleLabel;
and add these definitions of methods:
- (void) setPageNumber: (NSUInteger) newPageNumber{
pageNumber = newPageNumber;
}
- (NSUInteger) pageNumber{
return pageNumber;
}
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[titleLabel setText:[NSString stringWithFormat:#"Page %d", pageNumber]];
NSArray *images = [[NSBundle mainBundle] pathsForResourcesOfType:#".jpg" inDirectory:nil];
UIImage *img = [UIImage imageWithContentsOfFile:[images objectAtIndex:pageNumber-1]];
[imageView setImage:img];
}
Now go to the BookEffecViewController.h, and make it look like this:
#import <UIKit/UIKit.h>
#import "SinglePageViewController.h"
#interface BookEffectViewController : UIViewController
<UIPageViewControllerDataSource>
#property( nonatomic, strong) UIPageViewController *pageViewController;
#end
In BookEffectViewController.m revise viewDidLoad to look like this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageViewController.dataSource = self;
SinglePageViewController *singlePage = [[SinglePageViewController alloc] initWithNibName:#"SinglePageViewController" bundle:nil];
[singlePage setPageNumber:1];
NSArray *controllers = [[NSArray alloc] initWithObjects:singlePage, nil];
[self.pageViewController setViewControllers:controllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
self.pageViewController.view.frame = self.view.bounds;
}
Define two more methods in BookEffectViewController.m:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger previousPageNumber = [((SinglePageViewController*)viewController) pageNumber];
if ( previousPageNumber == 1 ) {
return nil;
}
else{
SinglePageViewController *singlePage = [[SinglePageViewController alloc] initWithNibName:#"SinglePageViewController" bundle:nil];
[singlePage setPageNumber:previousPageNumber-1];
return singlePage;
}
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger previousPageNumber = [((SinglePageViewController*)viewController) pageNumber];
if ( previousPageNumber >= [[[NSBundle mainBundle] pathsForResourcesOfType:#".jpg" inDirectory:nil] count] ) {
return nil;
}
else{
SinglePageViewController *singlePage = [[SinglePageViewController alloc] initWithNibName:#"SinglePageViewController" bundle:nil];
[singlePage setPageNumber:previousPageNumber+1];
return singlePage;
}
}
That's it. Command-R to test it with the iPad Simulator
This is a basic shell and other bells and whistles can be added for your own pleasure, i.e. take out Page Number, add a caption, etc.
Hope this helped.