UISearchBar - addSubview issue? - objective-c

I'm trying to add UISearchBar (fixed position!) on top of the UITableView.
CGRect rect = self.headerView.frame;
CGRect newRect = CGRectMake(0,
rect.origin.y + rect.size.height,
rect.size.width,
CZP_SEARCHBAR_HEIGHT);
UIView *view = [[UIView alloc] initWithFrame:newRect];
view.backgroundColor = [UIColor whiteColor];
Result (i got a white rect on position where i want my bar):
But if i want to add subview to my view, searchbar appear on 1st cell of tableview (below my view!)
[view addSubview:searchBar];

Here's one way to do it. It looks like you're trying to do it in code instead of a storyboard, so this is a code example. It also looks like you're doing it in a popover of sorts, I put together a quick project as an example that uses a popover, it doesn't look exactly like yours, but it's close enough to get you where you're trying to go I think.
First, here's the code sample, this is from the view controller that contains the header, search bar and tableview.
- (void)viewDidLoad
{
[super viewDidLoad];
// get the desired size for this popover and setup our header height
CGSize viewSize = self.preferredContentSize; // could also be self.view.bounds.size depending on where you're using it
CGFloat headerHeight = 44.0;
// setup our desired frames
CGRect headerFrame = CGRectMake(0, 0, viewSize.width, headerHeight);
CGRect searchContainerFrame = CGRectMake(0, headerHeight, viewSize.width, headerHeight);
// for this frame I'm simply centering it, there's better ways to do it but this is an example
CGRect searchBarFrame = CGRectMake(5, 5, searchContainerFrame.size.width - 10, searchContainerFrame.size.height - 10);
// set our tableview frame to be positioned below our header and search container frame
CGRect tableviewFrame = CGRectMake(0, headerHeight *2, viewSize.width, viewSize.height - (headerHeight * 2));
// create our header view and set it's background color
UIView *headerView = [[UIView alloc] initWithFrame:headerFrame];
headerView.backgroundColor = [UIColor orangeColor];
// create our container view to hold the search bar (not needed really, but if you want it contained in a view here's how)
UIView *searchContainer = [[UIView alloc] initWithFrame:searchContainerFrame];
searchContainer.backgroundColor = [UIColor greenColor];
// instantiate our search bar
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:searchBarFrame];
// add the search bar to the container view
[searchContainer addSubview:searchBar];
// create our tableview and position it below our header and search containers
UITableView *tableview = [[UITableView alloc] initWithFrame:tableviewFrame];
tableview.backgroundColor = [UIColor blueColor];
[self.view addSubview:headerView];
[self.view addSubview:searchContainer];
[self.view addSubview:tableview];
}
That snippet gives me a popover with an orange header, a green/grey search bar and a tableview beneath it.
EDIT: If you're interested in looking through the project file that I used to put this together you can download it off github here

Related

Best way to place a larger UIView inside smaller one by scaling the larger one to fit

I am grabbing a screen shot (as UIView object) of the view of my next controller and would like to place that screenshot inside a small rectangle in my preceeding controller's view (like a preview). What is the best way to place a large UIView object inside smaller one?
This did not work:
UIView *screenshot = .... // screenshot from the next controller's view
smallViewBox.contentMode = UIViewContentModeScaleAspectFit;
[smallViewBox addSubView:screenshot];
Try setting the bounds of the larger view to match the bounds of the smaller one. I just whipped up a quick example:
UIView *largeView = [[UIView alloc] initWithFrame:CGRectMake(40, 40, 60, 60)];
largeView.backgroundColor = [UIColor redColor];
[self.view addSubview:largeView];
UIView *smallView = [[UIView alloc] initWithFrame:CGRectMake(50,50,40,40)];
smallView.backgroundColor = [UIColor greenColor];
[self.view addSubview:smallView];
largeView.bounds = smallView.bounds;
If you comment out the largeView.bounds = smallView.bounds the green (smaller) box will be the only one visible because it is being drawn over the red box in the controller's view (The two views are siblings in this instance). To make the larger view a subview of the smaller one and restrict it to the smaller one's area you can do this:
UIView *largeView = [[UIView alloc] initWithFrame:CGRectMake(40, 40, 60, 60)];
largeView.backgroundColor = [UIColor redColor];
UIView *smallView = [[UIView alloc] initWithFrame:CGRectMake(50,50,40,40)];
smallView.backgroundColor = [UIColor greenColor];
[self.view addSubview:smallView];
largeView.frame = CGRectMake(0, 0, smallView.bounds.size.width, smallView.bounds.size.height);
[smallView addSubview:largeView];
This will result in the larger view's red color visible - covering the green smaller view's background. In this instance the large view is a child of the small view and occupies its entire area.
You could set a scale transform on it.
screenshot.transform = CGAffineTransformMakeScale(0.5, 0.5);

Why is there space between the bottom of the imageView and the first TableViewCell in the UITable?

I have a view controller that sets up a UIImageView and a UITableView as follows in viewDidLoad:
// Root UIView
UIView *rootView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
rootView.backgroundColor = [UIColor redColor];
self.view = rootView;
// Image View
NSString *path = [[NSBundle mainBundle] pathForResource:#"test320x180" ofType:#"JPG"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
self.imageView.image = image;
[self.view addSubview:self.imageView];
// Table View
self.tableView = [[UITableView alloc] initWithFrame:rootView.frame style:UITableViewStylePlain];
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.contentInset = UIEdgeInsetsMake(image.size.height, 0, 0, 0);
self.tableView.contentOffset = CGPointMake(0, -image.size.height);
self.tableView.backgroundColor = [UIColor clearColor]; // so we can see the image view
[self.tableView reloadData];
[self.view addSubview:self.tableView];
There are 20 pts between the bottom of the image view, which has CGRect: (0, 0, 320, 180), and the first cell of the Table View whose origin is (0, 200), shown in red in the screen shot below.
In reveal, I see that the TableView starts 20 pts below the ImageView, screenshot below. My best guess is that the table view automatically accounts for the status bar but the image view does not.
My intention is to have the image and the first tableview cell flush, but I'm not sure how to guarantee this without adding the magic number 20 to my code.
[[UIScreen mainScreen] applicationFrame] returns a value that includes the entire height of the screen, including the height of the status bar, and always in screen coordinates.
For example on a 3.5-inch iPhone, in both portrait and landscape, you'll get the same application frame:
{ 0,0, 320, 480 } // iPhone 3.5-inch, applicationFrame in both landscape and portrait
When you assign a frame with 0 for origin.y to the image view, then add it via:
[self.view addSubview:self.imageView];
... then the self.imageView's top 20 pixels are hidden beneath the status bar.
Please note, sometimes the status bar's height is doubled, such as by a phone call, or recording audio within an app, or Personal Hotspot/tethering. To survive that, you need the value from:
CGSize size = [[UIApplication sharedApplication] statusBarFrame].size;
This is returned in Screen coordinates, so the returned rect may be:
{ 0,0, 320, 20 } // iPhone 3.5-inch, portrait
{ 0,0, 20, 480 } // iPhone 3.5-inch, landscape
{ 0,0, 320, 40 } // iPhone 3.5-inch, portrait when tethering or other phone call
{ 0,0, 40, 480 } // iPhone
So the quick solution is to use something like the above CGRect size code with the following:
CGFloat height = (size.width < size.height? size.width : size.height);
3.5-inch, landscape when tethering or other phone call
Set the size of the frame of rootView using a CGRect that is
adjusted for the status bar height, so the imageView.frame.origin.y == 0 is
not hidden by the status bar
Set imageView's frame origin y to the status bar height (which means the rootView's content is still overlapped by the status bar).
UITableView is a subclass of UIScrollView so check the contentInset property and also set the UIViewController property automaticallyAdjustsScrollViewInsets is set to NO.
I think automaticallyAdjustsScrollViewInsets applies insets to all scroll views to account for status bars, button bars and navigation bars.
I faced this same issue, while i was working in storyboard where my tableview was having same 20 pixel gap. so what you need to do is:
1.) select your view controller and in attribute inspector deselect "Adjust scroll view inset". (this will remove the gap - that tableview is presuming for navigation bar)
2.) select your tableView and in size inspector set the "section header height" == '1' (this will remove the 20 pixel gap - that tableview is presuming for status bar)
Hope will help you!
Thanks

Pop up tutorial page like Photosynth's

I want to create something that is basically a clone of what photosynth does for their tutorial page. A small "?" button pops up what looks like a new view in a frame that is slightly smaller than the first view, so that you can still see the first view around the edges.
It's a little tough to see from the pic above, but the part around the edges is the old view that the tutorial display popped up over.
My first guess is that I need to use a container view somehow, but I can't find anything on the web about exactly how to do this. I can currently create a container view, hook it up to a new view controller via a segue, and do whatever I want in that new view controller, but the container view is always visible on the view it is contained within. Any help?
BTW, I'm using storyboarding with ARC.
You can add a transparent view to the key window, add a tap gesture recognizer that would dismiss it and the subviews to show the content:
#define OVERLAY_TAG 997
-(void)showTutorial
{
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *overlay = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
overlay.backgroundColor = [UIColor clearColor];
overlay.userInteractionEnabled = YES;
[keyWindow addSubview:overlay];
UITapGestureRecognizer * tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissTutorial)];
CGFloat border = 10;
CGRect frame = overlay.bounds;
// 20 is the status bar height (sorry for using the number)
frame = CGRectMake(border, border + 20, frame.size.width - border * 2, frame.size.height - border * 2 - 20);
// the black view in the example is probably a scroll view
UIView *blackView = [[UIView alloc] initWithFrame:frame];
blackView.backgroundColor = [UIColor blackColor];
blackView.alpha = 0.0;
[overlay addSubview:dimView];
// add all the subviews for your tutorial
// make it appear with an animation
[UIView animateWithDuration:0.3
animations:^{dimView.alpha = 1;}
completion:^(BOOL finished){[overlay addGestureRecognizer:tapRecognizer];}];
}
-(void)dismissTutorial
{
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *overlay = [keyWindow viewWithTag:OVERLAY_TAG];
[UIView animateWithDuration:0.3
animations:^{
overlay.alpha = 0.0;
}
completion:^(BOOL finished){
[overlay removeFromSuperview];
}];
}
This way you would remove the tutorial with a simple tap but you can use a button for instance.

UIScrollView metro theme

I am attempting to create a "metro" styled UIScrollView. It is similar to how iTunes app handles panels in the new ios version which wont be named.
I can't figure out how to have my views layout/scroll so that the next view in the sequence shows up. I've tried all sorts of things like keeping the contentSize the screen width but moving each view over -10ish so it will show up like above. I've tried making scrollView whose bounds were smaller than the screen so it would show the part of the next view. Nothing works.
Here is diagram of what I'm trying to do:
It seems extremely trivial on paper but I can't seem to get it work.
I'm not sure if I'm misinterpreting your requirements - but this might be a starting point to see how you could set it up:
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect viewBounds = self.view.bounds;
CGRect scrollViewFrame = CGRectMake(0, 0, floorf(CGRectGetWidth(viewBounds) / 2.2), CGRectGetHeight(viewBounds));
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:scrollViewFrame];
scrollView.center = self.view.center;
scrollView.contentSize = CGSizeMake(CGRectGetWidth(viewBounds) * 3, CGRectGetHeight(viewBounds) * 3);
scrollView.pagingEnabled = YES;
scrollView.clipsToBounds = NO;
UIPanGestureRecognizer *gestureRecognizer = scrollView.panGestureRecognizer;
[self.view addGestureRecognizer:gestureRecognizer];
for (int i = 0; i < 3; i++) {
CGRect frame = CGRectMake(10.f + (i * CGRectGetWidth(scrollView.bounds)), 10.f, CGRectGetWidth(scrollView.bounds) - 20.f, (CGRectGetHeight(scrollViewFrame) * 3) - 20.f);
UIView *view = [[UIView alloc] initWithFrame:frame];
view.backgroundColor = [UIColor redColor];
[scrollView addSubview:view];
}
[self.view addSubview:scrollView];
}
Literally just put this in an empty viewController's viewDidLoad:
The key things to note are
contentSize needs to be wide enough for all the panels
clipsToBounds should be NO so you can see the additional views
The bounds of the scrollview is essentially the main view port
pagingEnabled should be set
I've grabbed the panGestureRecognizer from the scrollview and attached it to the containing view instead so that panning is detected in the bounds of the containing view (which is larger) otherwise you are restricted to only detecting scrolls within the scrollviews bounds

UITableViewCell custom selectedBackgroundView background is transparent

I have the following code that creates a UIView that I assign to my UITableViewCell's selectedBackgroundView property. Everything works as expected, with the exception of the subview's background, which is transparent.
I use the same code to create a custom view that I assign to backgroundView, and that works fine.
What is causing that subview to be transparent for selectedBackgroundView, and how can I avoid that?
- (UIView*) makeSelectedBackgroundView
{
// dimensions only for relative layout
CGRect containerFrame = CGRectMake(0, 0, 320, 40);
UIView* containerView = [[UIView alloc] initWithFrame:containerFrame];
containerView.autoresizesSubviews = YES;
// dimensions only for relative layout
CGRect subframe = CGRectMake(5, 5, 310, 30);
UIView* subview = [[UIView alloc] initWithFrame:subframe];
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
subview.backgroundColor = [UIColor redColor];
subview.layer.cornerRadius = 5;
subview.layer.borderWidth = 2;
subview.layer.borderColor = [UIColor greenColor].CGColor;
[containerView addSubview:subview];
return containerView;
}
As we can see from name of ivar selectedBackgroundView, this background shown by cell when it was selected.
I've to reload few methods (– setSelected:animated: and – setHighlighted:animated:) of UITableViewCell subclass to reset background color of subviews back to their values. Look's like UIKit do some magic in this template methods (iterating over all UIView subclasses and set their background to clearColor)
This code might be helpful for you:
UIImageView *cellImageView = [[UIImageView alloc]
initWithFrame:CGRectMake(0,
0,
cell.frame.size.width,
cell.frame.size.height
)];
cellImageView.contentMode = UIViewContentModeScaleAspectFit;
// normal background view
[cellImageView setImage:[UIImage imageNamed:#"*<ImageName>*"]];
[cell addSubview:cellImageView];
[cell sendSubviewToBack:cellImageView];
[cellImageView release], cellImageView = nil;
Here cell is an object of custom UITableViewCell.
Also you can set backgroundColor property.
I would try to set the alpha for both containerView and subView to 1.0
[containerView setAlpha:1.0];
...
[subview setAlpha:1.0];
this should make your controls totally opaque.
You could also create some images for the background and use that images in state of creating 2 views. Let's say you create 2 image (normalBackground.png and selectedBackground.png) and then set this images as cell background. Here is a nice tutorial.
Try setOpaque:YES on your views.
In the end, I ended up subclassing UITableViewCell which contained a custom view object, and that worked.