I have created custom navigation bar and added one UIButton over it.
Please take a look at image attached.
I have UIButton with background image added as subview to Custom NavigationBar so portion above GREEN line is clickable not the portion below green line.
I have tried using UITapGestureRecognizer and also the touches methods ie. touchesBegan to UIImageView to handle the touch, but no luck.
I think this is only because my UIButton nontouchable potion is outside of NavigationBar Frame.
Is there any way to click the subview's portion which is ouside its parent view.
The parent view size is less than the Child view . That's why it is non clickable. Asper my knowledge only option you have is try to increase the parent view (Custom Navigationbar) frame size .
Yo need subclass Navigation Bar and add method hitTest:withEvent:. For example:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGRect extraTapRect = ...; // Put here needed rect
if (CGRectContainsPoint(extraTapRect, point)) {
for (UIView *subview in self.subviews.reverseObjectEnumerator) {
CGPoint subviewPoint = [subview convertPoint:point fromView:self];
UIView *result = [subview hitTest:subviewPoint withEvent:event];
if (result != nil) {
return result;
}
}
}
return [super hitTest:point withEvent:event];
}
I have an app where it reads an XML document containing 2 fields (image url and description). If the document has a field that has a valid image URL, I want the view to show the image. Else I just want it to show the description.
The problem I am having is :
how to show the UIImageView dynamically
how to move the UITextView downwards since now I added a UIImageView
Currently I just have a View with a UITextView on it.
Any ideas?
1) -[UIView setHidden:] if the UIImageView is already in place. Otherwise, [[UIImageView alloc] initWithFrame:], then add the image view as a subview to the view.
2) -[UIView setFrame:] (is one option)
The following is what you could do.
Prepare the the view with the UIImageView and the UITextView in the viewDidLoad like this:
-(void) viewDidLoad)
{
[super viewDidLoad];
myImageView = [[UIImageView alloc] init];
myImageView.frame = CGRectMake(x,y,0,0); //you can set it at the right position and even set either the width OR the height depending on where you want the textView in my example I'm gonna assume its beneath the imageView
//other imageView settings
[myView addSubView:myImageView];
myTextView = [[myTextView alloc] init];
myTextView.frame = CGRectMake(x,y,width,heigh+CGRectGetMaxY(myImageView.frame)); //here you prepare the textView for the imageView and the space it takes. But doesn't get hindered by it if it's not there.
//other textView settings.
[myView addSubView:myTextView];
//You would want this loading function about here OR after this loading has been occured.
[self loadImageFromURL];
}
-(void)loadImageFromURL
{
//get your image here.. or not
if(imageHasLoaded) //some condition that gets set when your image has successfully loaded. (This should be done in a delegate (didLoadImageFromURL) if you have such thing prepared.
{
//myImageView.image = theLoadedImage;
myImageView.frame = CGRectMake(x,y,theLoadedImageWidth,theLoadedImageHeight);
//after you set the frame of the imageView you need to refresh the view
[myView setNeedsDisplay];
}
}
And this is how you should dynamically add an imageView with image and frame to some view.
Right now I have 2 different UIPickerView in side my UITableViewController. I only show them upon tapping of certain cells in the table. What I'm trying to do is to hide the pickers whenever I touch outside the pickers. Is there a delegate method or something similar to achieve this? I prefer to keep my controller as a UITableViewController instead of a simple UIViewController since I have a textView in one of the cells and scrolling after the keyboard shows is just a bit too much in a UIViewController.
Thanks in advance.
One of the Possible solutions is that when a particular cell is tapped and you handle picker (to present the picker), you can insert a view called as MASK View over the tableview. (with Frame as self.tableview.frame - yourPicker.frame.size.height ). Now when ever you get any click on this view you can handle it as follows
-(void)showMaskView{
if (!viewMaskView) {
CGRect viewRect = CGRectMake(0, 0, self.tableView.frame.size.width, self.tableView.frame.size.height - yourPicker.frame.size.height);
viewMaskView = [[MaskView alloc] initWithFrame:viewRect];
viewMaskView.delegate = self;
}
[self.view addSubview:viewMaskView];
[self.view bringSubviewToFront:viewMaskView];
}
-(void)removeMaskView{
if (viewMaskView) {
[viewMaskView removeFromSuperview];
}
//Remove the Picker
}
In the MaskView class you can handle the touch as follows
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if(self.delegate && [self.delegate respondsToSelector:#selector(removeMaskView)])
[self.delegate removeMaskView];
}
you can see the colored mask view over the Picker in the image. When tapped it removes picker.
I have made a custom UIView which is shown when the user hits a button in the navigationbar. I make my view's in code. In my loadview I set the autoresizing masks and the view loads correct on screen. However the UIView which is shown when the user taps the button does not resize even when I have set the autoresizing masks.
UIView *blackView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 416.0)];
blackView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Do I need to use self.view.frame.size.width and self.view.frame.size.height instead? And if I do why? Does not resizing masks work outside of loadView?
Thank you for your time:)
the autoresizingMask affects how a view will behave when its superviews frame changes. if all you are doing is showing theblackViewwhen you tap a button, thenblackView` will have whatever frame you initially set for it.
If this isn't enough info, please post some more code around how you are configuring and displaying blackView and it's superview and explain more about what situations you are expecting blackView to resize in. Rotation is one of them, if that's what you're concerned with.
First things first, I hope you've done this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Let's say the view that needs resizing is: view2
The view that has view2 as a subview is: view1
While creating view1 you would declare it as:
view1 = [[UIView alloc] init];
[view1 setNeedsLayout];
Now in view1's .m file you need to overload the layoutSubviews method as shown:
- (void)layoutSubviews
{
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
In case view1 is a view controller's view, you need to do that same thing as above in the willRotate method as shown
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
This is a tried and tested method that I use to handle orientation changes.
I'm writing an iPhone app that (like most apps) supports auto-rotation: You rotate your phone, and its views rotate and resize appropriately.
But I am assigning a custom view to navigationItem.titleView (the title area of the navigation bar), and I can't get that view to resize correctly when the phone rotates.
I know what you're thinking, "Just set its autoresizingMask to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight," but it's not that simple. Of course, if I don't set my view's autoresizingMask, then my view doesn't resize; and I want it to resize.
The problem is, if I do set its autoresizingMask, then it resizes correctly as long as that view is visible; but the titleView's size gets messed up in this scenario:
Run the app, with the phone held in portrait mode. Everything looks good.
Do something that causes the app to push another view onto the navigation stack. E.g. click a table row or button that causes a call to [self.navigationController pushViewController:someOtherViewController animated:YES].
While viewing the child controller, rotate the phone to landscape.
Click the "Back" button to return to the top-level view. At this point, the title view is messed up: Although you are holding the phone in landscape mode, the title view is still sized as if you were holding it in portrait mode.
Finally, rotate the phone back to portrait mode. Now things get even worse: The title view shrinks in size (since the navigation bar got smaller), but since it was already too small, now it is much too small.
If you want to reproduce this yourself, follow these steps (this is a bit of work):
Make an app using Xcode's "Navigation-based Application" wizard.
Set it up so that the top-level table view has rows that, when you click them, push a detail view onto the navigation stack.
Include this code in both the top-level view controller and the detail view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
Include this code in only the top-level view controller:
- (void)viewDidLoad {
[super viewDidLoad];
// Create "Back" button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Master"
style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
// Create title view
UILabel* titleView = [[[UILabel alloc] initWithFrame:CGRectMake(0,0,500,38)] autorelease];
titleView.textAlignment = UITextAlignmentCenter;
titleView.text = #"Watch this title view";
// If I leave the following line turned on, then resizing of the title view
// messes up if I:
//
// 1. Start at the master view (which uses this title view) in portrait
// 2. Navigate to the detail view
// 3. Rotate the phone to landscape
// 4. Navigate back to the master view
// 5. Rotate the phone back to portrait
//
// On the other hand, if I remove the following line, then I get a different
// problem: The title view doesn't resize as I want it to when I:
//
// 1. Start at the master view (which uses this title view) in portrait
// 2. Rotate the phone to landscape
titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.navigationItem.titleView = titleView;
}
Finally, follow my repro steps.
So ... am I doing something wrong? Is there a way to make my titleView always resize correctly?
You should also set the contentMode of the UIImageView to get the titleView properly displayed in landscape and/or portrait mode :
imgView.contentMode=UIViewContentModeScaleAspectFit;
The whole sequence: (self is a UIViewController instance)
UIImageView* imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myCustomTitle.png"]];
imgView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imgView.contentMode=UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = imgView;
[imgView release];
I had something similar - but it was returning (popping) to root view controller. Ultimately, I went with the following for popping:
[[self navigationController] setNavigationBarHidden:YES animated:NO];
[[self navigationController] popViewControllerAnimated:YES];
[[self navigationController] setNavigationBarHidden:NO animated:NO];
And it worked. There may have been a better way but - after all the hours I'd already spent on this issue - this was good enough for me.
I dealt with this same issue by keeping track of the customView's initial frame, then toggling between that and a scaled CGRect of the initial frame in a -setLandscape method on a UIButton subclass. I used the UIButton subclass as navigationItem.titleView and navigationItem.rightBarButtonItem.
In UIButton subclass -
- (void)setLandscape:(BOOL)value
{
isLandscape = value;
CGFloat navbarPortraitHeight = 44;
CGFloat navbarLandscapeHeight = 32;
CGRect initialFrame = // your initial frame
CGFloat scaleFactor = floorf((navbarLandscapeHeight/navbarPortraitHeight) * 100) / 100;
if (isLandscape) {
self.frame = CGRectApplyAffineTransform(initialFrame, CGAffineTransformMakeScale(scaleFactor, scaleFactor));
} else {
self.frame = initialFrame;
}
}
Then in the InterfaceOrientation delegates I invoked the -setLandscape method on the customViews to change their sizes.
In UIViewController -
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self updateNavbarButtonsToDeviceOrientation];;
}
- (void)updateNavbarButtonsToDeviceOrientation
{
ResizeButton *rightButton = (ResizeButton *)self.navigationItem.rightBarButtonItem.customView;
ResizeButton *titleView = (ResizeButton *)self.navigationItem.titleView;
if (self.interfaceOrientation == UIDeviceOrientationPortrait || self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) {
[rightButton setLandscape:NO];
[titleView setLandscape:NO];
} else {
[rightButton setLandscape:YES];
[titleView setLandscape:YES];
}
}
(Answering my own question)
I got this working by manually keeping track of the titleView's margins (its distance from the edges of the navigtion bar) -- saving when the view disappears, and restoring when the view reappears.
The idea is, we aren't restoring the titleView to the exact size it had previously; rather, we are restoring it so that it has the same margins it had previously. That way, if the phone has rotated, the titleView will have a new, appropriate size.
Here is my code:
In my view controller's .h file:
#interface MyViewController ...
{
CGRect titleSuperviewBounds;
UIEdgeInsets titleViewMargins;
}
In my view controller's .m file:
/**
* Helper function: Given a parent view's bounds and a child view's frame,
* calculate the margins of the child view.
*/
- (UIEdgeInsets) calcMarginsFromParentBounds:(CGRect)parentBounds
childFrame:(CGRect)childFrame {
UIEdgeInsets margins;
margins.left = childFrame.origin.x;
margins.top = childFrame.origin.y;
margins.right = parentBounds.size.width -
(childFrame.origin.x + childFrame.size.width);
margins.bottom = parentBounds.size.height -
(childFrame.origin.y + childFrame.size.height);
return margins;
}
- (void)viewDidUnload {
[super viewDidUnload];
titleSuperviewBounds = CGRectZero;
titleViewMargins = UIEdgeInsetsZero;
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Keep track of bounds information, so that if the user changes the
// phone's orientation while we are in a different view, then when we
// return to this view, we can fix the titleView's size.
titleSuperviewBounds = self.navigationItem.titleView.superview.bounds;
CGRect titleViewFrame = self.navigationItem.titleView.frame;
titleViewMargins = [self calcMarginsFromParentBounds:titleSuperviewBounds
childFrame:titleViewFrame];
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Check for the case where the user went into a different view, then
// changed the phone's orientation, then returned to this view. In that
// case, our titleView probably has the wrong size, and we need to fix it.
if (titleSuperviewBounds.size.width > 0) {
CGRect newSuperviewBounds =
self.navigationItem.titleView.superview.bounds;
if (newSuperviewBounds.size.width > 0 &&
!CGRectEqualToRect(titleSuperviewBounds, newSuperviewBounds))
{
CGRect newFrame = UIEdgeInsetsInsetRect(newSuperviewBounds,
titleViewMargins);
newFrame.size.height =
self.navigationItem.titleView.frame.size.height;
newFrame.origin.y = floor((newSuperviewBounds.size.height -
self.navigationItem.titleView.frame.size.height) / 2);
self.navigationItem.titleView.frame = newFrame;
}
}
}
For IOS5 onwards, as this is an old question...This is how I accomplished the same issue with the title text not aligning properly.
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2 forBarMetrics:UIBarMetricsLandscapePhone];
Tested on ios5/6 sims works fine.
This is what I did:
self.viewTitle.frame = self.navigationController.navigationBar.frame;
self.navigationItem.titleView = self.viewTitle;
The viewTitle is a view created in the xib, it takes the size of the navigationBar and after it has been added the titleView adjust the size to leave room to the back button. Rotations seem to work fine.
I had had same problem, but I seem to get workaround with following code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIView *urlField = self.navigationItem.leftBarButtonItem.customView;
CGRect frame = urlField.frame;
frame.size.width = 1000;
urlField.frame = frame;
}
In my case, the custom view is a UITextField, but I hope this will help you.