Best way to change UIViewController view NIB programmatically - interface-builder

I have this iPad application with different NIBs and views.
Each view has two NIBs, one for potrait orientation and one for landscape, so i'm searching for a method to programmatically switch NIB for a given UIViewController .
Right now i have this function that i'm using in the willRotateToInterfaceOrientation method of each controller :
void UIHandleRotation( UIViewController *controller, NSString *nibName, UIInterfaceOrientation orientation, BOOL transform ){
double angle = 0.0;
if( orientation == UIInterfaceOrientationPortrait ) {
angle = PI * 2 /* 360° */;
}
else if( orientation == UIInterfaceOrientationLandscapeLeft ){
angle = PI + PI/2 /* 270 ° */;
// Each landscape nib is [nib-name]L
nibName = [NSString stringWithFormat:#"%#L", nibName];
}
[[NSBundle mainBundle] loadNibNamed:nibName owner:controller options:nil];
if( transform ) {
controller.view.transform = CGAffineTransformMakeRotation( angle );
}
}
But it's giving me strange behaviour (ui controls position messed, the outlets are not associated as in interface builder, and so forth).
Am i doing something wrong or there's just a better way to implement this ?
Thanks
NOTE: I'm not using a navigation controller, so i can't use this solution Easiest way to support multiple orientations? How do I load a custom NIB when the application is in Landscape?

You should use only one NIB with two views, one for portrait(potraitView) with frame(0,0,768, 1024)and
another for landscape(landScapeView) with frame(0,0,1024, 768).
And set your controls in both views (potraitView and landScapeView) according to your requirements.
and after this you can set your views according to device orientation as-
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
landScapeView.hidden = YES;
portraitView.hidden = NO;
}
else{
landScapeView.hidden = NO;
portraitView.hidden = YES;
}
return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
Hi,
As I think you want to know, when the view loads, what is the default orientation, and How I can load the view according to this orientation.
If this question is same as you are asking, then the answes is-
When you run your application and the same class view loads, then - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
method is called automatically. Then it returns your device orientation, and sets your view according to this orientation(using the same code as described above).
I think you need not to do more.

You shouldn't switch nibs, you should switch controllers.
See this answer for: Easiest Way to Support Multiple Orientations

you need to have one view per nib, so your UIViewController will have two UIViews. one for portrait, and one for landscape.

Why do you need to use 2 nibs for the same view.You should use one nib for different orientations.You can handle the required controls programatically in the same method
willRotateToInterfaceOrientation
Thanks.

Related

Landscape views only loading properly from portrait and after rotating, why?

new iOS developer here. I have multiple views that require different images to be displayed in portrait and landscape. I currently have implemented that successfully and the portrait image loads fine, and, upon rotation, the landscape image also loads fine. However, if the device is in landscape orientation then switches to another view, it loads improperly - wrong size, resolution, alignments, etc. My code for dealing with orientation changes is below:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if((self.interfaceOrientation == UIDeviceOrientationLandscapeLeft) || (self.interfaceOrientation == UIDeviceOrientationLandscapeRight))
{
_image1.image = [UIImage imageNamed:#"Landscape.png"];
}
else if((self.interfaceOrientation == UIDeviceOrientationPortrait) || (self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown))
{
_image1.image = [UIImage imageNamed:#"Portrait.png"];
}
}
I believe it is because the method is only called upon rotation. If I rotate the improper, initial landscape view, for instance, it displays the correct images once again. Is there a way to get the method to run and load the proper landscape view when the initial orientation is in landscape? Or a way to force the correct image to display? Thanks much.
I finally fixed this issue by adding an orientation checker. I added the following in my .h:
#property (nonatomic, readonly) UIDeviceOrientation *orientation;
Then I added this to my .m file in the viewDidLoad method:
if(([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) || ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)) {
_image1.image = [UIImage imageNamed:#"Landscape.png"];
}
This checks if the initial orientation is landscape. If it is, it loads my Landscape.png image. Otherwise, since the default image is my Portrait.png, as set in the Storyboard, that loads if the orientation is already in portrait. Cheers!
EDIT: The above code is not advised as you can run into issues when using it, such as with orientation-locked devices. I changed the it to check for the status bar's orientation, rather than the device's orientation, as below:
if(([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft) ||
([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight)) {
_image1.image = [UIImage imageNamed:#"Landscape.png"];
}
You do not need to declare any variables in the .h, and just add the above in the viewDidLoad method.

select a view by clicking osx 10.6

i created a image view
for(int i=0; i<pcount; i++)
{
int x = rand() % 350;
int y = rand() % 350;
NSRect rect = NSMakeRect((x+10),(y+10), 200, 200);
//NSImageView *imageView
imageView1 = [[NSImageView alloc]initWithFrame:rect];
[imageView1 setTag:i];
// imageView = [[NSImageView alloc]initWithFrame:rect];
// [imageView1 rotateByAngle:rand() % 150];
[imageView1 setImageScaling:NSScaleToFit];
[imageView1 canBecomeKeyView];
NSImage *theImage = [[NSImage alloc]initWithContentsOfURL:(NSURL*)[patharray objectAtIndex:(i)]];
[imageView1 setImage:theImage];
[[imageView1 cell] setHighlighted:YES];
[[layoutCustom view] addSubview:imageView1 positioned:NSWindowMovedEventType relativeTo:nil];}
now how can select each image view by mouse click ? thanks in advance.
I'm assuming here that you have your reasons for not using existing collection views. So from what I read in your code you have layoutCustom.view, which contains a bunch of NSImageViews. Here are two options:
In your layoutCustom object implement the mouseDown: (or mouseUp: or both). Take the event location convert it view coordinates and look for any subview for which CGRectContainsPoint(subview.frame, mouseDownPoint) return YES. You should select that view.
Subclass NSImageView and implement mouseDown: (or mouseUp: or both). On mouseDown: simply set a "selected" flag. Either the view can draw something itself when selected or the layoutCustom object can observe the property and draw the selection accordingly.
I would prefer option 1 because it simpler, requires fewer classes and fewer interactions between objects.
// Option 1 (in layoutCustom class)
- (void) mouseDown:(NSEvent*)theEvent {
CGPoint mouseDownPoint = [self convertPoint:theEvent.locationInWindow fromView:nil];
for (NSView *view in self.subviews) {
if (CGRectContainsPoint(view.frame, mouseDownPoint)) {
// Do something to remember the selection.
// Draw the selection in drawRect:
[self setNeedsDisplay:YES];
}
}
}
// Option 2 (in Custom subclass of NSImage)
- (void) mouseDown:(NSEvent*)theEvent {
self.selected = !self.selected;
}
// Option 2 (in layoutCustom class)
- (void) addSubview:(NSView*)view positioned:(NSWindowOrderingMode)place relativeTo:(NSView*)otherView {
[super addSubview:view positioned:place relativeTo:otherView];
[self startObservingSubview:view];
}
- (void) willRemoveSubview:(NSView*)view {
[self stopObservingSubview:view];
}
- (void) startObservingSubview:(NSView*)view {
// Register your KVO here
// You MUST implement observeValueForKeyPath:ofObject:change:context:
}
- (void) stopObservingSubview:(NSView*)view {
// Remove your KVO here
}
I've got a better idea: Instead of fighting with converting mouse clicks in a view to coordinates and then figuring out how to map it to the right subview or sub-image, why not have one big (or scrolling?) view and then add your images as giant "NSButton" objects (set to custom type), where the button images can be the images you want to add.
As for how to select each image? You can either subclass "NSButton" and keep track of some custom data within it, or you can use a "tag" to figure out which button was pressed in your "IBAction" method and then decide what to do with it.
Another approach might be to embed your images into NSTableView cells...

user interaction enabled subviw in scrollview

I have a ScrollView that contains a ContainerView. The ContainerView contains another View that the user is supposed to be able to pan around in.
The scrollView scrolls vertical only, the "view inside the containerView" is panable in all directions.
Here is what I have
self.scrollView.contentSize = CGSizeMake(1024,1440);
self.modelController = [self.storyboard instantiateViewControllerWithIdentifier:#"LCProduct3DViewController"];
self.modelController.meshIdentifier = self.meshIdentifier;
[self addChildViewController:self.modelController];
self.modelController.view.frame = self.threeDView.bounds;
[self.threeDView addSubview:self.modelController.view];
What happens is that the touch events inside the modelController's view and the ones outside the modelControler's view but inside the scrollview bounds seems to be getting the the way of each other.
I played around with
self.scrollView.canCancelContentTouches = NO;
self.scrollView.delaysContentTouches = NO;
but havent found a working solution yet.
Any Ideas ?
Thanks in advance
as can be found in other places:
The trick is to subclass the scrollView and override the hitTest method like this:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView* result = [super hitTest:point withEvent:event];
if ([result isKindOfClass:[GLKView class]]) {
self.scrollEnabled = NO;
} else {
self.scrollEnabled = YES;
}
return result;
}
this way - if the hittest for the innver view is positive, scrolling for the scrollview is disabled and only the innver view will get the event.

UIPageViewController - how to customize left and right page in landscape mode?

I have a page-based application in Xcode and I am trying to set it up so that the left page and right page have different layouts in landscape mode.
Specifically, I want to set a different page bg image if the page is the on the left side, so that it looks more like a real book.
Is there a way to programmatically detect if a UIPageViewController is on the left or right side in landscape mode? How would you go about solving this problem?
If you have a property for the background image, you can change it on the viewControllerAtIndex
- (YourPageContentViewController *)viewControllerAtIndex:(NSUInteger)index {
// Return the data view controller for the given index.
if (([self.modelArray count] == 0) || (index >= [self.modelArray count])) {
return nil;
}
YourPageContentViewController *dataViewController [[YourPageContentViewController alloc]initWithNibName:#"YourPageContentViewController" bundle:nil];
if (index%2 ==0) {
//set a background
dataViewController.backgroundImage = imageForLeftSide;
} else {
//set the other background
dataViewController.backgroundImage = imageForRightSide;
}
dataViewController.dataObject = [self.modelArray objectAtIndex:index];
return dataViewController;
}

Core Data Image Won't Load Into NSTableView Image Cell

In my code I am storing an image into my Core Data model (works fine). If I set up my view to have an NSImageView and bind its Data to Controller Key: selection and modelKeyPath: myImagePath, it works. It will show each image for the selected row.
I then create a new column in my NSTableView and drag an image cell onto the column. However, I cannot get my Core Data bindings to have the image show up in the cell. I have tried binding both value and data, but no luck.
Since I am sure the image is stored properly, what am I doing wrong in my binding to prevent the image from showing up in the table cell?
Thanks so much.
(My background: new Cocoa developer who has recently gone through the entire Hillegass book.)
I'm not sure what is happening to cause your problem but there's an easy way to find out. Hook up an NSValueTransformer to the binding. Then in that transformer you can log stuff to find out if you're passing a nil value, or you can transform your data value into an NSImage and pass that back… basically you can do whatever you want in the transformer class. Here's one I use on image data in a core-data model.
#interface DataToImageTransformer : NSValueTransformer {
}
#end
#implementation DataToImageTransformer
+ (Class)transformedValueClass {
return [NSImage class];
} // the class of the return value from transformedValue:
+ (BOOL)allowsReverseTransformation {
return YES;
} // if YES then must also have reverseTransformedValue:
- (id)transformedValue:(id)value {
if (value == nil || [value length] < 1) return nil;
NSImage* i = nil;
if ([value isKindOfClass:[NSData class]]) {
i = [NSKeyedUnarchiver unarchiveObjectWithData:value];
}
return i;
}
- (id)reverseTransformedValue:(id)value {
if (value == nil) return nil;
NSData* d = nil;
if ([value isKindOfClass:[NSImage class]]) {
d = [NSKeyedArchiver archivedDataWithRootObject:value];
}
return d;
}
#end
In AppController class you initialize the transformer:
+ (void)initialize {
DataToImageTransformer *transformer = [[DataToImageTransformer alloc] init];
[NSValueTransformer setValueTransformer:transformer forName:#"DataToImageTransformer"];
[transformer release];
}
Then in Interface Builder you put "DataToImageTransformer" in for the binding. Now you have control over the binding and can do as I explained earlier in the transformer class. Note that I'm using NSKeyedArchiver to convert an NSImage to data and back again but you can use tiffRepresentation or any other method you want in its place.
If anyone is having issues with the suggested technique with Xcode 6. I created a Swift project, using the Document based app. With an ImageWell control (NSImageView), bindings work directly without value the need of a transformer.
A detail though, put you binding on Data binding of the view itself, not on the ImageCell objet under it.

Resources