i use two blocks to draw some buttons on my subview dynamically. One calculates frames for Portrait mode and the other does for Landscape. It works well but when i rotate, it writes over the old ones. Hence, some of my buttons come twice. Here is my code of detecting the oriantation:
//i have defined blocks in viewDidLoad: and everything is ok till here
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
dispatch_async(dispatch_get_main_queue(), PortraitBlock);
}
else
{
dispatch_async(dispatch_get_main_queue(), LandscapeBlock);
}
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight ||
interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
Now, how can i clean the view i add the buttons on?
Note: I add buttons on a UIView object, and that object too is on a UIScrollView object
hi,.,
Try below code with this all your button which you alloc on the view
will be remove.
for(UIButton *view in yourview.subviews)
{
[view removeFromSuperview];
}
Before removing the views from your view first check the class and then remove it. After that you can add your buttons on the screen based on the orientation.
for(UIView *view in [View to add buttons].subviews)
{
if ([view isKindOfClass:[UIButton class]]) [view removeFromSuperview];
}
If you are using any customButton the mention your customButtonClassName in place of UIButton.
Don't add new buttons at all. Just change the old frames.
If you need to add new buttons. Just remove the old ones!? To remove all subviews, you could use:
for(UIView* view in self.view.subviews)
{
[view removeFromSuperview];
}
Related
I am a newbie in iOS-programming (trying to convert my android-app to iOS).
I am using the storyboard and want to create a view, which has on the top a scrollview (with an ImageView inside) and on the bottom there is a TextView. This should be the View, if the users use the portrait-orientation.
Now I want that the ViewCOntroller in the landscape-orientation only shows the scrollview and the imageviews (that are inside the scrollview). The textview should be hidden in landscape.
Is it possible to make the images bigger, but hold the aspect ratio ( in android:adjustViewBounds) ?
How can I handle this? I've tried it by using the "autosizing", but the result wasn't good.
Apple provides a method that is called when the app will change its orientation.
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[textview setHidden:YES];
} else {
[textview setHidden:NO];
}
}
Just add it to your view controller. Check more on Apple's reference
I've looked through various SO questions on the topic and I have not found a solution. I have a UIViewController with a UITableView and a UICollectionView. I want the UICollectionView to scroll to the top, when the user taps it.
The documents say if you have more than one UiScrollView subclass - you need to set them to no and the UiScrollView you want to scroll to the top, to yes.
So I wrote this bit of code to go through all my views:
for (UIScrollView *view in self.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
view.scrollsToTop = NO;
}
}
self.collectionView.scrollsToTop = YES;
This way I am sure any subclass of UiScrollView has it's scrollsToTop property set to no.
However tapping on the status bar does not do anything.
Can someone tell me what I am missing here?
Thank you
It seems that you are only iterating through the subviews of your main view. Your UITableView may be nested inside another view. Try doing the following;
//in view did load
[self setScrollToTopFalse:self.view];
self.collectionView.scrollsToTop = YES;
-(void)setScrollToTopFalse:(UIView *)v
{
for (UIView * v1 in [v subviews]) {
if ([[v1 class]isSubclassOfClass:[UIScrollView class]]) {
((UIScrollView *)v1).scrollsToTop = NO;
}
[self setScrollToTopFalse:v1];
}
}
I shift a modal view up when the keyboard is displayed to prevent some parts of the interface being hidden by the keyboard.
When the view has been shifted, the toolbar cancel / save buttons don't respond to taps. Taps inside the modal are detected and respond fine.
I've set it up so that the keyboard should dismiss when tapping outside the textfield, but this doesn't work when tapping on the navigation bar either.
How can I respond appropriately to taps on barbuttonitems when the view has been offset?
Here's how I am shifting the modal up when the keyboard is displayed:
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
int movementDistance;
float movementDuration;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if(
UIInterfaceOrientationIsLandscape(self.interfaceOrientation)
)
{
//code for landscape shift - not relevant because you can't see the toolbar
else{
NSLog(#"Portrait for animation");
movementDistance = IPAD_PORTRAIT_KEYBOARD_HEIGHT;
movementDuration = KEYBOARD_ANIMATION_DURATION;
if(up){
keyboardAppearedInLandscape = false;
}else{
//the keyboard is going down
NSLog(#"Keyboard going down");
//is the iPad in the same orientation now that it was when it came up?
if ([[UIDevice currentDevice] orientation] == UIInterfaceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIInterfaceOrientationPortraitUpsideDown)
{
if (!keyboardAppearedInLandscape) {
//don't do anything - the keyboard is being dismissed in the same way it was called. It's much the same in any case.
}else{
movementDistance = IPAD_LANDSCAPE_KEYBOARD_HEIGHT;
}
}
}
}
}
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
//end text field movy-ness
And here is how I am detecting taps outside the textfield to dismiss the keyboard:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
//keyboard goes away if you tap somewhere else on screen
NSLog(#"resignFirstResponder");
[self.view endEditing:YES];
}
[super touchesBegan:touches withEvent:event];
}
You won't receive touch events outside the original rectangle bounds, because...
SuperView thinks that the view hasn't changed location
View is moving up within itself instead of from outside
Visual elements on the screen are changing but aren't being recognized
Instead, to move the subview properly:
SuperView should be responding to the UIKeyboard
Utilize UIKeyboardWillHideNotification && UIKeyboardWillShowNotification
Set ClipsToBounds to YES
Now you won't see stuff that's outside of the view's bounds.
Makes debugging more effective
Check out:
Add done button to a UINavigationBar #StackOverflow
I think your problem is that you are moving the view up within itself, rather than from outside. So, the visual elements on the screen are changing, but according to your SuperView, the view is in the same place, so you will only get touch events inside the original rectangle (this rectangle is called the "bounds").
Instead, your SuperView should be responding to UIKeyboardWillShowNotification and UIKeyboardWillHideNotification to move the subview to the proper place.
You can debug this kind of stuff more easily if you set ClipsToBounds to YES. This will make it so you can't see stuff that is outside of the view's "bounds" (the area that can receive touch events.)
Which is the best way to change the UI when orientation is changed?
For example, use two different UIView one portrait and a landscape and show one of both if orientation is changed, or use one UIView and change the UI control sizes and positions?
Any other ideas?
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
NSLog(#"Change to custom UI for landscape");
}
else if (toInterfaceOrientation == UIInterfaceOrientationPortrait ||
toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
NSLog(#"Change to custom UI for portrait");
}
}
I always recommend using the autoresizingmask for all the subviews in the view controller view. With this being set correctly all the views will resize automatically from the orientation and you don't need the extra rotation specific subviews (one portrait view and one landscape view).
The documentation says that if I want to support both portrait and landscape, I basically have two ways of doing that:
Set up the viewcontroller's view so that the subviews autoresize correctly and make smaller changes programmatically at runtime
If the changes are more substantial, create an alternative landscape interface and push/pop the alternative modal viewcontroller at runtime
I would like to present the info where the layout is substantially different, but logic is the same. Ideally, I would load another XIB for the same viewcontroller, but it does not seem to be an option.
Sounds like #2 is what I need to do, but my problem with that is that it sounds like it would use the standard modalviewcontroller animations that are nothing like the device rotation animation. (Of course, being the lazywebber that I am, I did not test this hypothesis.)
So, how do I load an alternative layout for landscape with the same viewcontroller but different XIB? Should I use the method #2 above and is the rotation animation natural? Or is there some other way?
I instantiate my UIView instances in -viewDidLoad: and add them as subviews to the view controller's view property:
- (void) viewDidLoad {
[super viewDidLoad];
self.myView = [[[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 280.0f, 210.0f)] autorelease];
// ...
[self.view addSubview:myView];
}
I then call -viewWillAppear: to center those subviews:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self adjustViewsForOrientation:[[UIDevice currentDevice] orientation]];
}
I also override -willRotateToInterfaceOrientation:duration:
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)newInterfaceOrientation duration:(NSTimeInterval)duration {
[self adjustViewsForOrientation:newInterfaceOrientation];
}
The -adjustViewsForOrientation: method sets the center CGPoint of various subview objects, depending on the device's orientation:
- (void) adjustViewsForOrientation:(UIInterfaceOrientation)orientation {
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
myView.center = CGPointMake(235.0f, 42.0f);
// ...
}
else if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
myView.center = CGPointMake(160.0f, 52.0f);
// ...
}
}
When the view controller is loaded, the UIView instances are created and positioned based on the device's current orientation. If the device is subsequently rotated, the views are re-centered to new coordinates.
To make this smoother, one could probably use a keyed animation in -adjustViewsForOrientation:, so that the subviews more gracefully move from one center to the other. But the above works for me, for now.