My app is building purely programmatically on UITabBarController above a UINavigationController, which are both declared in my AppDelegate. Inside my navigationcontroller, I'm showing a UIViewController, a custom class. This custom class should show a custom UIView in a UIScrollView and a UIPageControl.
Heres my problem:
self.view seems to create an EXC_BAD_ACCESS error when I call it without declaring self.view = [[UIView alloc] init] (or similar). I was wondering if this was a problem with -(void) loadView but seems like it produces the same error in -(void)viewDidLoad. So I basically had to use self.view = scrollView to even show my scrollView, considering [self.view addSubview:scrollView] produced an error. My UIPageControl should stay on the page all the time, and actually be another part of the view than the UIScrollView. So I tried to add a container-view like this
Code:
container = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,0,0)];
scrollView.pagingEnabled = YES;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.showsHorizontalScrollIndicator = NO;
// EDIT: ofcourse, I'm also resizing the frame with [scrollView setContentSize:] later, but this is farfetched code to add here.
[container addSubview:scrollView];
self.view = container;
Unfortunately, it seems that I don't get any result at all, and what appears is just an empty view. However, if I add a UILabel or similar, it shows:
[container addSubview:[[UILabel alloc] initWithFrame:CGRectMake(50,50,50,50)]]; // entered above or beneath the addSubview:scrollView line.
My question is: Why doesn't my scrollView appear in the UIView container?
I've noticed that some tutorials say that scrollView must have a delegate, and I agree with the logic - however I can't seem to find out how I set that delegate when I am in my CustomUIViewController-class instead of my AppDelegate.
after you change the UIScrollView size you should use:
[scrollView setNeedsDisplay:YES];
also you implement Delegates the same way you do in other classes:
.h:
#interface MyClass : NSObject <UIScrollViewDelegate>
Okay, the problem seemed to be the initialization - I didn't realize that frame and content was two different things. Seems like the frame that is initializing the view should be whatever size the view should fill, while content is the actual content of whatever should be scrolled. So when I was having problems with user interaction, it was really this.
The problem of why it didn't show in the first place was (stupid.) that the frame was initially, and never changed from, 0,0 so I really lied in my first post.
Thanks to UIScrollView and PageControl: space between views who solved my problem with user interaction.
My steps was to backtrace from self.view:
NSLog(#"%f\n%f",
((UIScrollView*) [[self.view subviews] objectAtIndex:0]).frame.size.width,
((UIScrollView*) [[self.view subviews] objectAtIndex:0]).frame.size.height);
when I realized these were 0 and 0, fixing the problem wasn't too hard :) thanks though, for your efforts Kristian.
Related
I'm now building up an iPad app which uses UISplitViewController and on the detail view controller I want to initialize two UITableView. However, for some strange reasons even if I tried to initialize those two table views in the same way, only the second table view is initialized properly.
Here's my code that initializes the table views:
_t1 = [[UITableView alloc] initWithFrame:CGRectMake(30, 400, 300, 600)];
_t1.delegate = self;
_t1.datasourse = self;
[self.view addSubview:_t1];
_t2 = [[UITableView alloc] initWithFrame:CGRectMake(360, 400, 300, 600)];
_t2.delegate = self;
_t2.datasourse = self;
[self.view addSubview:_t2];
I set those two table views as #property (strong, nonatomic) in my header file of the view controller. I don't think I've made any mistakes so far.
However, here's the image of the resultant detail view controller. The first table view starts populating its cells after some space is filled at the top of the table view. It looks like the redundant space's height is same to the height of the navigation bar, but when I tried to output self.view before the addSubview: method on the above code, I got the same result saying self.view = <UIView: 0x10a3dd920; frame = (0 0; 768 1024); autoresize = W+H; layer = <CALayer: 0x10a3dd800>>. So, I think it's added to the same parent view.
So what am I missing? And how can I fix the issue up? I suspect it's related to either navigation controller or split view controller, but am not sure.
I use iOS 7.1 and Xcode 5.1.
Maybe this will solve your problem
self.automaticallyAdjustsScrollViewInsets = NO;
I'm not sure what is the purpose of that property but it definitely messes a lot of UIScrollViews and UITableViews.
I haven't found a lot of documentation but my theory is that usually you have one uitableview or one uiscrollview as first child of your view. This subview usually takes the whole frame of your view. In that case, i guess you don't have to bother with different topbar (translucent, opaque, ios6). But still that's a lot of assumptions, and i don't think that should be the default behavior.
Hi I was learning how to use autoresizing and scroll view also and came up with this solution which works, just your feedback would be welcome on my approach if it is ok how it could be improved etc.
I just created a test view controller whose view looks like this.
The grey area is a UIView called mainView and it is subview of view controller's view (and it's longer than view controller's view).
When I changed rotation one would see such thing:
You can see the calculator does not fit on the screen anymore.
So I decided to have a UIScrollView already from start. Add mainView as a subview to it.
Final thing that was remaining, I wanted the frame of the ScrollView to change also when device rotates, so that I could get such effect:
(you see the scroll views width is same as phones).
This is how I implemented all this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:[self.view bounds]];
[[self view] addSubview:scrollView];
[scrollView addSubview:self.mainView];
[scrollView setContentSize:[mainView bounds].size];
[scrollView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleBottomMargin];
}
Any comments and feedback?
You can use the didRotate methods to position your buttons how you want in the different orientations. There is a good tutorial here: http://www.youtube.com/watch?v=f3yb24f8O1Y&list=PL53038489615793F7
There are a bunch of related questions here, but none that feels like a concise or correct answer. Here's the situation:
I am creating a new ViewController and don't want to use a nib file. My understanding from the Apple docs is that if I don't want to use a nib, I should implement loadView to manually create my own view hierarchy.
However, its not clear to me how I should properly instantiate self.view with the proper bounds (given this view controller might be used in a bunch of different situations, setting it simply to the screen bounds doesn't feel right).
Somehow the default UIViewController loadView does seem to properly initiate the frame size, but its not clear if I'm writing my own version what I should be doing to do this.
There is no need to implement loadView. Instead, implement viewDidLoad and create and add any and all desired subviews you want. Just add them to the default self.view.
If you want to add a view that fills the view controller's view then do something like the following:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *someView = [[UIView alloc] initWithFrame:self.view.bounds];
someView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:someView];
}
In loadView, you should set the view property of viewController, and nothing else. Adding subviews should be done in viewDidLoad.
- (void)loadView {
self.view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 80, 40)];
}
Roopesh Chander has an interesting blog post on which strategy to choose: loadView vs viewDidLoad
for Programmatic UI Setup. He recommends setting the frame in loadView rather than viewDidLoad for maximum efficiency.
I'd like to place an ADBannerView object onto my UITableView screen statically, what means that I want it to always stay above my toolbar (self.navigationController.toolbar), even when the user is scrolling the tableview. I've solved this by adding by ADBannerView as a subview to my toolbar and given it negative values for the frames origin:
[self setBannerViewSize];
[self.navigationController.toolbar addSubview:bannerView];
The only problem is: I can't click and open the iAd this way - I can see the banner but nothing happens when I tap on it.
Since I'm also using a refreshControl, the option to use a UIViewController instead of UITableViewController and add a tableView manually wouldn't work for me. Is there any other way I can get my ADBannerView statically showing in my table view controller AND still being tappable?
Thank you in advice!
Yay!! After all I succeeded in solving this (really annoying) problem by myself (and a lot of reading around)!
First, I found this really world-changing post. Basically this post handles with the topic that a UITableViewController uses self.view for its tableView property, so overriding the tableView property (or synthesizing it manually) plus giving self.view a new view (from application) and adding tableView as its subview would make it possible to reach the real superview of tableView.
But this still didn't solve my problem, although I was sure it would, because it all made sense. My bannerView appeared in the right place (and was fixed) but it still didn't do anything when clicked. But there was a second minor thing I didn't know about:
As I read in this post the superview of a subview doesn't only have to be userInteractionEnabled but also have a non-transparent backgroundColor. Because my superviews background color was set to [UIColor clearColor] it all didn't work - but setting its backGroundColor to e.g. blackColor solved the whole problem: the bannerView got finally tappable! :)
So, my code is now looking like this:
#synthesize tableView;
- (void)viewDidLoad
{
[super viewDidLoad];
if (!tableView && [self.view isKindOfClass:[UITableView class]]) {
tableView = (UITableView *)self.view;
}
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
self.tableView.frame = self.view.bounds;
[self.view addSubview:self.tableView];
[self resizeTableToFitBanner];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:bannerView];
// some other code
}
BannerViewController in Apple's iAdSuite sample code solves this problem very elegantly:
https://developer.apple.com/library/ios/samplecode/iAdSuite/Introduction/Intro.html
I think you should use a container view, and set things up in IB. You can add a tool bar and ADBannerView to the bottom of the view of your navigation controller's root view controller. Fill the rest of the space with a container view - this will give you an embedded view controller automatically. You should delete this one and then drag in a tableViewController and control drag from the container view to the tableViewController to hook up the embed segue.
How do I change the order of views displayed, when the two views I want to rearrange are not related?
I am trying to work on someone else's code, and they have a class which is a subclass of UITableViewController. I want to put a background in, so I put it as a subview of self.view .
The problem is, it's displayed in front of the table, and the table is located as self.tableView, not self.view.tableView . Since the two views aren't together, I can't use the sendSubviewToBack method.
self is the viewController class I'm working on.
Any ideas?
UIImageView *background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"editInfoBG.png"]];
background.frame = CGRectMake(0, 0, 320, 416);
[self.view addSubview:background];
// Doesn't work, the table is not a subview of self.view
[self.view sendSubviewToBack:background];
If you want to add subviews into your viewcontroller, you should use a subclass of UIViewController with manually added UITableView as subview and implementing the UITableViewDataSource and UITableViewDelegate protocols instead of using UITableViewController.
you can insert the background before the tabeview like this
[self.view insertSubview:background belowSubview:self.tableView.view];