Loading views from NIB to use as pages in a scrollview cancels autolayout - objective-c

I've searched this issue, but did not managed to find a working answer.
I need to show a paging scrollview that shows some views loaded from .xib files, here's my code:
This is the init code for TutorialPage.m, subclass of UIView:
- (id)initWithFrame:(CGRect)frame andPage:(int) page
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"tutorial page %d initialized", page);
switch (page) {
case 0:
self.view = [[[NSBundle mainBundle] loadNibNamed:#"TutorialPage01View" owner:self options:nil] objectAtIndex:0];
break;
case 1:
self.view = [[[NSBundle mainBundle] loadNibNamed:#"TutorialPage02View" owner:self options:nil] objectAtIndex:0];
break;
default:
break;
}
NSLog(#"frame %g %g, %g %g", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
[self addSubview:self.view];
}
return self;
}
The pages are added to the scrollview as follows, the code below is part of TutorialViewController.m, that has a valid scrollview outlet:
- (void)viewDidLayoutSubviews
{
NSLog(#"tutorial view loaded");
self.scrollView.scrollEnabled = YES;
self.scrollView.pagingEnabled = YES;
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * 2, self.scrollView.frame.size.height);
for (int i = 0 ; i < 2 ; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIView *subview = [[TutorialPage01 alloc] initWithFrame:frame andPage:i];
[self.scrollView addSubview:subview];
}
}
The layouts are divided in two halves, vertically. And each half has constraints to make it match the container view and have equal heights.
THE PROBLEM IS
When the views are added to the scrollview they kinda ignore the autolayout, and the subview that should have a size of (290, 538), presents itself as (320,568), so it invades the other pages.
Is it possible to add constraints in the .xib file and then have then applied to the superview it is added, in code?
Thanks in advance!

This is a quick and dirty way to do it. Since you're adding a new frame programmatically, you should also ensure that the view you are replacing conforms to the frame you are initiating.
- (id)initWithFrame:(CGRect)frame andPage:(int) page
{
self = [super initWithFrame:frame];
if (self) {
UIView *view = [[[NSBundle mainBundle] loadNibNamed:#"TutorialView" owner:self options:nil] objectAtIndex:0];
view.frame = self.frame;
[self addSubview:view];
}
return self;
}
This seemed to work for me in a much simpler app I built based on your code.

Related

Find where UIimageview frame is being set

I am working on an iPad application in Xcode 5 for a jigsaw puzzle game. I created the layout in storyboard, adding uiimageviews for each of 9 frames that would hold part of the image that was going to be the puzzle, and placed them on the view controller. Someone else has since removed the storyboard from the game and I need to go back and finish up the puzzle without it. The biggest issue I have is that the position of the frames seem to be being set somewhere outside of the view controller.h or .m classes and I can't find it. I am able to change the position of the pieces using image1.center in the touchesmoved method, but not in the viewdidload. I have been trying to track down where this is being set for several hours and am having no luck. Setting any movement of the center in viewdidload,viewwillappear, viewdidappear etc do not change the position as expected.
Can anyone either guide me on a way to change the image1.center after the view loads but without any user input, or else tell me how to trace all changes that are occurring in the view? I don't think seeing my code will help in this question since it is not seemingly changed in the classes that I am looking at, but I will post what works and what doesn't. I am only posting the portions that touch the uiimageview (1-9)
from .h
#implementation PuzzleViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
#interface PuzzleViewController : UIViewController
{
UIImageView *image1;
UIImageView *image2;
UIImageView *image3;
UIImageView *image4;
UIImageView *image5;
UIImageView *image6;
UIImageView *image7;
UIImageView *image8;
UIImageView *image9;
UIImage *puzzlePic;
}
from .m
- (void)viewDidLoad
{
[super viewDidLoad];
//set up buttons to accept user input
image1.userInteractionEnabled = true;
image2.userInteractionEnabled = true;
image3.userInteractionEnabled = true;
image4.userInteractionEnabled = true;
image5.userInteractionEnabled = true;
image6.userInteractionEnabled = true;
image7.userInteractionEnabled = true;
image8.userInteractionEnabled = true;
image9.userInteractionEnabled = true;
// Do any additional setup after loading the view from its nib.
puzzlePic = [LLFileManager getImage:#"star7.gif" folder:#"animation_images"];
[self placeImage];
}
// handle touches within images to move
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if([touch view] == image1){
image1.center = touchLocation;
}
else if([touch view] == image2){
image2.center = touchLocation;
}
else if([touch view] == image3){
image3.center = touchLocation;
}
else if([touch view] == image4){
image4.center = touchLocation;
}
else if([touch view] == image5){
image5.center = touchLocation;
}
else if([touch view] == image6){
image6.center = touchLocation;
}
else if([touch view] == image7){
image7.center = touchLocation;
}
else if([touch view] == image8){
image8.center = touchLocation;
}
else if([touch view] == image9){
image9.center = touchLocation;
}
}
// splits image into 9 equal parts, takes input of a ui image
-(NSArray *)splitImage9:(UIImage *)im{
NSMutableArray *images = [NSMutableArray array];
CGSize imageSize = im.size;
CGFloat xPos = 0.0, yPos = 0.0;
CGFloat width = imageSize.width/3;
CGFloat height = imageSize.height/3;
for (int y = 0; y < 3; y++) {
xPos = 0.0;
for (int x = 0; x < 3; x++) {
CGRect rect = CGRectMake(xPos, yPos, width, height);
CGImageRef cImage = CGImageCreateWithImageInRect([im CGImage], rect);
UIImage *dImage = [[UIImage alloc] initWithCGImage:cImage];
[images addObject:dImage];
xPos += width;
}
yPos += height;
}
return images;
}
// position pieces on board load
-(void)positionPieces{ // test, not working
CGPoint piece = CGPointMake(50,100);
image2.center = piece;
}
// places the cut images into view placeholders
-(void)placeImage{
NSArray *puzzleImage = [self splitImage9: puzzlePic];
[image1 setImage:[puzzleImage objectAtIndex:0]];
[image2 setImage:[puzzleImage objectAtIndex:1]];
[image3 setImage:[puzzleImage objectAtIndex:2]];
[image4 setImage:[puzzleImage objectAtIndex:3]];
[image5 setImage:[puzzleImage objectAtIndex:4]];
[image6 setImage:[puzzleImage objectAtIndex:5]];
[image7 setImage:[puzzleImage objectAtIndex:6]];
[image8 setImage:[puzzleImage objectAtIndex:7]];
[image9 setImage:[puzzleImage objectAtIndex:8]];
}
The biggest issue I have is that the position of the frames seem to be being set somewhere outside of the view controller.h or .m classes and I can't find it.
If the storyboard is gone, and if no code has been added to put the image views into the interface, then the interface should be empty. The reason you are seeing the interface at all must be because you are accidentally running an outdated version of the project.
To solve that problem, clean the project as I explain here:
How to Empty Caches and Clean All Targets Xcode 4
You will then find, when you run the project again, that the interface is now empty and the mystery is solved: the frames have no position, because there are no frames, because there are no image views at all.

Does it make sense to add ATMHud to tabBarController.view

When i try to add ATMHud to uitableviewcontroller subview it does work but it doesn't disable the scrolling and if the tableview is not on top i can't see the hud view. What i did was added to teh tabBarController.view that works but i want find out if this is a good idea or later on i might have issues with it.
Another question is tabBarController.view frame is that the whole screen or just the bottom part. How come atmhud shows in the middle of the screen?
Thanks in advance!
Yan
============
Found a blog post that shows how to reset self.view and add tableview separately in uitableviewcontroller
UITableViewController and fixed sub views
- (void)viewDidLoad {
[super viewDidLoad];
if (!tableView &&
[self.view isKindOfClass:[UITableView class]]) {
tableView = (UITableView *)self.view;
}
self.view = [[[UIView alloc] initWithFrame:
[UIScreen mainScreen].applicationFrame] autorelease];
self.tableView.frame = self.view.bounds;
self.tableView.contentInset = UIEdgeInsetsMake(44.0, 0.0, 0.0, 0.0);
[self.view addSubview:self.tableView];
UIView *fixedBar = [[UIView alloc] initWithFrame:
CGRectMake(0.0, 0.0, self.view.bounds.size.width, 44.0)];
fixedBar.backgroundColor = [UIColor colorWithRed:
0.0 green:1.0 blue:0.0 alpha:0.7];
[self.view addSubview:fixedBar];
[fixedBar release];
}
After this when add hud to self.view you will be able to disable the tableview on the bottom.
Let me know if this a good way to setup the tableview
The problem with using the tab bar is that the hud is now modal, and the user cannot change the tab.
It sounds like the tableview is not your primary viewm, as it can get "covered up". If its not the primary view, then add the ATMHud to self.view. If the tableView is the same as self.view, then add a new transparent view to it, then add the HUD to that view.
The tabBarController.view is the view that hosts the tabbed views - if you want to see its size (or frame) log it using NSStringFromCGRect(self.tabBarController.frame);
EDIT: I just did a test, the ATMHud DOES block the UI. All I can think of is that you have not inserted it where you need to (at the top of current view's subviews.) I have a demo project where I do this:
hud = [[ATMHud alloc] initWithDelegate:self];
[self.view addSubview:hud.view];
[hud setCaption:#"Howdie"];
[hud setActivity:YES];
[hud show];
[hud hideAfter:5];
A button under the hud is not active - in fact nothing in the view is active (probably the Nav Bar would be live though)
If you want an ARCified and field tested version, you can grab it here
EDIT2: The solution to your problem is below. Note that ATMHud blocks clicks from getting to the table, and the code below stops the scrolling:
- (void)hudWillAppear:(ATMHud *)_hud
{
self.tableView.scrollEnabled = NO;
}
- (void)hudDidDisappear:(ATMHud *)_hud
{
self.tableView.scrollEnabled = YES;
}
Dump the views:
#import <QuartzCore/QuartzCore.h>
#import "UIView+Utilities.h"
#interface UIView (Utilities_Private)
+ (void)appendView:(UIView *)v toStr:(NSMutableString *)str;
#end
#implementation UIView (Utilities_Private)
+ (void)appendView:(UIView *)a toStr:(NSMutableString *)str
{
[str appendFormat:#" %#: frame=%# bounds=%# layerFrame=%# tag=%d userInteraction=%d alpha=%f hidden=%d\n",
NSStringFromClass([a class]),
NSStringFromCGRect(a.frame),
NSStringFromCGRect(a.bounds),
NSStringFromCGRect(a.layer.frame),
a.tag,
a.userInteractionEnabled,
a.alpha,
a.isHidden
];
}
#end
#implementation UIView (Utilities)
+ (void)dumpSuperviews:(UIView *)v msg:(NSString *)msg
{
NSMutableString *str = [NSMutableString stringWithCapacity:256];
while(v) {
[self appendView:v toStr:str];
v = v.superview;
}
[str appendString:#"\n"];
NSLog(#"%#:\n%#", msg, str);
}
+ (void)dumpSubviews:(UIView *)v msg:(NSString *)msg
{
NSMutableString *str = [NSMutableString stringWithCapacity:256];
if(v) [self appendView:v toStr:str];
for(UIView *a in v.subviews) {
[self appendView:a toStr:str];
}
[str appendString:#"\n"];
NSLog(#"%#:\n%#", msg, str);
}
#end

Custom UITableViewCell does not appear on second load

I have a custom uitableview with custom uitableviewcell in my view cell. The cell doesn't show on second load. That is, when I go from this view to another and back to this view.
This is how I load my table in my view.
CropTableViewController *cropTblViewCon = [[CropTableViewController alloc]init];
self.cropTableViewController = cropTblViewCon;
cropTableViewController.view.frame = cropTblView.frame;
[cropTblView removeFromSuperview];
[self.view addSubview:cropTableViewController.view];
self.cropTblView = cropTableViewController.tableView;
This is how I go another view
AppDelegate_iPhone *appDelegate = [[UIApplication sharedApplication] delegate];
AddPestReportStepOne *report = [[AddPestReportStepOne alloc]init];
[appDelegate.navCon pushViewController:report animated: YES];
[report release];
This is how I load my table cell in my custom table view.
static NSString *CellIdentifier = #"CustomCropCell";
CustomCropCell *cell = (CustomCropCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
tableView.scrollEnabled = NO;
// tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
NSArray *topLevelObjects = [[NSBundle mainBundle]
loadNibNamed:#"CustomCropCell" owner:nil options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (CustomCropCell *) currentObject;
break;
}
}
}
//if (counter<6 ) {
CropEntity *crop = [[CropEntity alloc] init];
crop=[ cropList objectAtIndex:counter];
NSString *imgStr = [NSString stringWithFormat:#"%#%#%#",domainName,cropImagePath, [crop cropImage]];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imgStr]]];
CGSize size = CGSizeMake(80, 80);
[cell setBackgroundColor: [UIColor clearColor]];
image = [Utilities scale:image toSize:size ];
[cell.columnOne setTag: [[crop cropId] integerValue] ];
[cell.columnOne setTitle:[crop cropName] forState:UIControlStateNormal];
[cell.columnOne setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[cell.columnOne setTitleEdgeInsets:UIEdgeInsetsMake(0.0, -image.size.width, -75.0, 0.0)]; // Left inset is the negative of image width. counter = counter+ 1;
[cell.columnOne setImage:image forState:UIControlStateNormal];
[cell.columnOne setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, -cell.columnOne.titleLabel.bounds.size.width)]; // Right inset is the negative of text bounds width.
[cell.columnOne setBackgroundColor: [UIColor clearColor]];
counter = counter + 1;
It seems that the rows are not removed. My cellforrowat indexpath keep increasing...
Okay, i'm assuming you want to add a UITableView to a ViewController. Here are the steps for this:
(1) You have create an UIViewController, whether by code or interface builder
(2) Create an UITableView, and add it to your UIViewController
UITableView *tableView = [[UITableView alloc]
initWithFrame:CGRectMake(0, 0, 320, 440)
style:UITableViewStylePlain];
// Set up the tableView delegate and datasource
[self.view addSubview:tableView];
[tableView release];
I'm sure there is a reason why you are adding the tableView to a UIViewController (there is not reason to have an extra UITableViewController. If not just use a UITableViewController, and set up a UITableView.
Also, keep in mind where you create the tableView at. For instance, if you create it in init or viewDidLoad and using a UINavigationController, when you hit the back button on the UINavigationController the ViewDidLoad or init isn't getting called. Try it yourself, put a NSLog statement to test.
So either use viewDidAppear or [tableView reloadData];
Edit:
Think about your code you create a UITableViewContoller then you remove its UITableView from it's super view (does not make sense) and then you add the UITableViewControllers view (the UITableView) to self.view which is i'm guessing a UIViewController. Surely this can't be what you are trying to do.
I would recommend reading Apple's View Programming guide for a better understanding of UIViewControllers and Views.
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaViewsGuide/Introduction/Introduction.html

Scroll to xib (pagecontrol)

i had a small question, i'm using this code to scroll from color to color:
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor], [UIColor blueColor], nil];
you can scroll from color to color, but is there a way to scroll to another xib file?
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor], [UIColor blueColor], nil];
for (int i = 0; i < colors.count; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
subview.backgroundColor = [colors objectAtIndex:i];
[self.scrollView addSubview:subview];
[subview release];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * colors.count, self.scrollView.frame.size.height);
}
This tutorial will help you out: http://www.iosdevnotes.com/2011/03/uiscrollview-paging/
(i think that you've use this tutorial)
but on the last line of the tutorial:
Update: In the comments, some people have asked about placing buttons
inside the scroll view, and also about setting up the scroll view
using Interface Builder. I’ve added some code that includes buttons
here, and a version using Interface Builder here.
So here is the link to move to xib files: https://github.com/cwalcott/UIScrollView-Paging/tree/buttons-ib
Good luck,
Nathan
Instead of preparing UIView's and adding them to a scrollview, you could indeed use [[NSBundle mainBundle] loadNibNamed... to load several nib (which are compiled xib) and add them to a scrollview, and then scroll them up and down, or back and forth.

cocoa hello world screensaver

I have been studying NSView and as such I thought I would give a shot at a screen saver. I have been able to display and image in an NSView but I can't seen to modify this example code to display a simple picture in ScreenSaverView.
http://www.mactech.com/articles/mactech/Vol.20/20.06/ScreenSaversInCocoa/
BTW great tutorial that works with Snow Leopard.
I would think to simply display an image I would need something that looked like this...
What am I doing wrong?
//
// try_screensaverView.m
// try screensaver
//
#import "try_screensaverView.h"
#implementation try_screensaverView
- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview
{
self = [super initWithFrame:frame isPreview:isPreview];
if (self) {
[self setAnimationTimeInterval:1]; //refresh once per sec
}
return self;
}
- (void)startAnimation
{
[super startAnimation];
NSString *path = [[NSBundle mainBundle] pathForResource:#"leaf" ofType:#"JPG" inDirectory:#""];
image = [[NSImage alloc] initWithContentsOfFile:path];
}
- (void)stopAnimation
{
[super stopAnimation];
}
- (void)drawRect:(NSRect)rect
{
[super drawRect:rect];
}
- (void)animateOneFrame
{
//////////////////////////////////////////////////////////
//load image and display This does not scale the image
NSRect bounds = [self bounds];
NSSize newSize;
newSize.width = bounds.size.width;
newSize.height = bounds.size.height;
[image setSize:newSize];
NSRect imageRect;
imageRect.origin = NSZeroPoint;
imageRect.size = [image size];
NSRect drawingRect = imageRect;
[image drawInRect:drawingRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1];
}
- (BOOL)hasConfigureSheet
{
return NO;
}
- (NSWindow*)configureSheet
{
return nil;
}
#end
NSRect bounds = [self bounds];
NSSize newSize;
newSize.width = bounds.size.width;
newSize.height = bounds.size.height;
[image setSize:newSize];
I don't know why you're doing this.
NSRect imageRect;
imageRect.origin = NSZeroPoint;
imageRect.size = [image size];
A.k.a. [self bounds].size.
NSRect drawingRect = imageRect;
[image drawInRect:drawingRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1];
i.e., [image drawInRect:[self bounds] fromRect:[self bounds] operation:NSCompositeSourceOver fraction:1].
If you're trying to draw the image at its natural size, there's no reason to ever send it a setSize: message. Cut out that entire first part, and the rest should work just fine.
If you're trying to fill the screen (which would be scaling, which would contradict the comment), set the drawingRect to [self bounds], not imageRect. This does exactly as it reads:
image,
draw into (the bounds of the view),
from (the image's entire area).
[image
drawInRect:[self bounds]
fromRect:imageRect
⋮
];
Neither the natural-size-fixed-position draw nor the full-screen draw is an effective screen saver. The latter is irredeemable; you can make the former useful by animating the image around the screen.
I had a similar issue, so I'll post my solution. OP was attempting to load image contents via NSBundle's mainBundle. Instead, you'll have more luck fetching the screensaver's bundle and loading files from there, like so:
NSBundle *saverBundle = [NSBundle bundleForClass:[self class]];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[saverBundle pathForResource:#"image" ofType:#"png"]];
Since you are doing your drawing in animateOneFrame, try removing your overridden drawRect.