I am struggling to enable zooming in a UIPopoverController using a UIScrollView. The scrollView is 600x600 and it should display a view controller that displays a UIImageView. The image appears except it's not centered.
This is the code from the viewDidLoad method in the view controller that is displayed in the popover.
- (void)viewDidLoad {
self.imageView = [[UIImageView alloc] init];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.image = self.image;
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.backgroundColor = [UIColor greenColor];
[self.scrollView setContentSize:self.imageView.image.size];
[self.scrollView setDelegate:self];
[self.imageView setFrame:CGRectMake(0, 0, 600, 600)];
self.view = self.scrollView;
[self.scrollView addSubview:self.imageView];
// Do any additional setup after loading the view.
}
The UIScrollView and UIImageView are declared as properties inside the UIViewController that's displayed in the popover. image is another property that is set to point to an image when the UIViewController is created.
This is what is looks like.
I would like to center the image in the popover and make it fit. How can I do that? Thanks.
what you need to do is:
first:
in your uiviewcontroller which is presented as a popover
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
preferredContentSize = CGSize(width: collectionPreference.bounds.width, height: collectionPreference.bounds.height)
println("I am appearing?")
}
then from the viewcontroller that is presenting the popover:
make it conform to UIPresentationStyleForPresentingController protocol,
and implement this two things:
number one:
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
number two: in your prepareforsegue method:
if let identifier = segue.identifier {
if identifier == "segueToYourPopoverVC" {
if let pVC = segue.destinationViewController as? YourPopoverVC {
let ppc = pVC.popoverPresentationController
ppc?.delegate = self
}
}
}
best of luck
Related
I'm working on an app where we use the white status bar tint and a dark background for the navigationBar. There is one scene where we want the navigationBar hidden but it also takes away the background color for the status bar. Is there a simple solution to keep a dark background up with hiding the navigationBar at the same time?
My code to hide the navigation bar is:
[self.navigationController setNavigationBarHidden:YES];
or in Swift:
self.navigationController?.navigationBarHidden = true
Assuming you are developing for iOS7+: The statusbar doesn' have any background color on its own. In fact, the reason you are seeing a dark background when the navigation bar is visible, is because it extends upwards underneath the statusbar. So if you want to keep the status bar background you can simply add a view with an appropriate background color to the current scene (viewController, window , etc.). Give it a frame of UIApplication.sharedApplication.statusBarFrame.
-- UPDATE 1 --
Sample code (Swift 3) Gives you a solid black status bar background:
class ViewController: UIViewController {
private let statusBarUnderlay = UIView()
override func viewDidLoad() {
super.viewDidLoad()
statusBarUnderlay.backgroundColor = UIColor.black
view.addSubview(statusBarUnderlay)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
statusBarUnderlay.frame = UIApplication.shared.statusBarFrame
}
}
-- UPDATE 2 --
While we're at it. The above code is not how you should lay out your views. Instead, subclass UIView and do your layout there. Then override loadView of your UIViewController subclass and return an instance of your custom view.
class View: UIView {
private let statusBarUnderlay = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
statusBarUnderlay.backgroundColor = UIColor.black
addSubview(statusBarUnderlay)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Storyboards are incompatible with truth and beauty.")
}
override func layoutSubviews() {
super.layoutSubviews()
statusBarUnderlay.frame = UIApplication.shared.statusBarFrame
}
}
All I needed to do was add a secondary view and set it to below the status bar like this:
-(void)viewForStatusBar {
UIView *view = [UIView new];
[self.view addSubview:view];
view.backgroundColor = [UIColor blackColor];
view.frame = CGRectMake(0, -20, [UIScreen mainScreen].bounds.size.width, 20);
}
Or in Swift:
func viewForStatusBar() {
let view = UIView()
self.view.addSubview(view)
view.backgroundColor = .blackColor()
view.frame = CGRectMake(0, -20, UIScreen.mainScreen().bounds.size.width, 20)
}
Here's the implementation of LTNavigationBar
I think it may help you with.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y;
if (offsetY > 0) {
if (offsetY >= 44) {
[self setNavigationBarTransformProgress:1];
} else {
[self setNavigationBarTransformProgress:(offsetY / 44)];
}
} else {
[self setNavigationBarTransformProgress:0];
self.navigationController.navigationBar.backIndicatorImage = [UIImage new];
}
}
- (void)setNavigationBarTransformProgress:(CGFloat)progress
{
[self.navigationController.navigationBar.transform = CGAffineTransformMakeTranslation:(0, -44 * progress)];
}
It makes the navigation bar hidden and status bar have the same background color as navigation bar when scrolling the view.If you don't need scrolling, you can just call [self setNavigationBarTransformProgress:1]
I have a pageViewController - I would like to add a scrollview with an image view behind it and while I scroll the pages in my pageViewController - the background should scroll in the same direction but with a lower see. I use auto-layout in storyboards:
so I add the pageViewController:
pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:nil];
pageController.delegate = self;
pageController.dataSource = self;
[self addChildViewController:pageController];
CGRect pageFrame = self.view.frame;
pageFrame.origin.y += 50.f;
pageFrame.size.height -= 50.f;
pageController.view.frame = pageFrame;
pageController.view.backgroundColor = [UIColor clearColor];
[self.view addSubview:pageController.view];
get it's scrollview:
for (UIView *possibleScrollView in pageController.view.subviews) {
if ([possibleScrollView isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)possibleScrollView).delegate = self;
}
}
and listening for its delegate:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(#"%f", scrollView.contentOffset.x);
[parlaxScrollView setContentOffset:CGPointMake(scrollView.contentOffset.x * .2f, scrollView.contentOffset.y) animated:NO];
}
And here I have some confusing results when I scrolling to the second page:
378.500000
386.500000
403.500000
419.000000
448.000000
469.000000
...
747.000000
750.000000
375.000000 ///!!!!!THE CONTENT OFFSET RETURNED TO THE INITIAL VALUE!!!!!
Why does my content view offset reset? Wha't wrong with it?
5 years later, sorry for the delay 😅
Maybe other people will still have the same issue.
You are not supposed to change the delegate of the page view controller's scroll view. It can break its normal behaviour.
Instead, you can:
Add a pan gesture to the page view controller's view:
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panRecognized(gesture:)))
view.addGestureRecognizer(panGesture)
panGesture.delegate = self
Add the new function in order to know how the view is being scrolled.
#objc func panRecognized(gesture: UIPanGestureRecognizer) {
// Do whatever you need with the gesture.translation(in: view)
}
Declare your ViewController as UIGestureRecognizerDelegate.
Implement this function:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
I think you need to keep track of which page is currently being displayed and add the width to the content offset.
something like this:
let currentPage:Int
I'm trying to create a tableview programmatically that has a search bar in the tableHeaderView. For some reason the search bar appears on top of the first cell.
I'm using Masonry to build constraints.
Can someone point me to what i'm doing wrong.
- (void)setupViews {
...
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.tableView];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
self.tableView.tableHeaderView = self.searchBar;
...
}
- (void)updateViewConstraints {
[self.searchBar mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.view);
make.height.equalTo(#(44));
}];
[self.tableView mas_updateConstraints:^(MASConstraintMaker *make) {
make.self.top.equalTo(self.view);
make.self.bottom.equalTo(self.toolbar.mas_top);
make.width.equalTo(self.view);
}];
...
}
You can see here that the header is at the same level as the cells.
Thanks for your help, I found a gist on GitHub which talked about changing the size of tableViewHeader using AutoLayout:
https://gist.github.com/andreacremaschi/833829c80367d751cb83
- (void) sizeHeaderToFit {
UIView *headerView = self.tableHeaderView;
[headerView setNeedsLayout];
[headerView layoutIfNeeded];
CGFloat height = [headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
headerView.frame = ({
CGRect headerFrame = headerView.frame;
headerFrame.size.height = height;
headerFrame;
});
self.tableHeaderView = headerView;
}
If I call this method during updateViewConstraints then it works.
However, I don't fully understand it.
Using Extension in Swift 3.0
extension UITableView {
func setTableHeaderView(headerView: UIView?) {
// prepare the headerView for constraints
headerView?.translatesAutoresizingMaskIntoConstraints = false
// set the headerView
tableHeaderView = headerView
// check if the passed view is nil
guard let headerView = headerView else { return }
// check if the tableHeaderView superview view is nil just to avoid
// to use the force unwrapping later. In case it fail something really
// wrong happened
guard let tableHeaderViewSuperview = tableHeaderView?.superview else {
assertionFailure("This should not be reached!")
return
}
// force updated layout
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
// set tableHeaderView width
tableHeaderViewSuperview.addConstraint(headerView.widthAnchor.constraint(equalTo: tableHeaderViewSuperview.widthAnchor, multiplier: 1.0))
// set tableHeaderView height
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
tableHeaderViewSuperview.addConstraint(headerView.heightAnchor.constraint(equalToConstant: height))
}
func setTableFooterView(footerView: UIView?) {
// prepare the headerView for constraints
headerView?.translatesAutoresizingMaskIntoConstraints = false
// set the footerView
tableFooterView = footerView
// check if the passed view is nil
guard let footerView = footerView else { return }
// check if the tableFooterView superview view is nil just to avoid
// to use the force unwrapping later. In case it fail something really
// wrong happened
guard let tableFooterViewSuperview = tableFooterView?.superview else {
assertionFailure("This should not be reached!")
return
}
// force updated layout
footerView.setNeedsLayout()
footerView.layoutIfNeeded()
// set tableFooterView width
tableFooterViewSuperview.addConstraint(footerView.widthAnchor.constraint(equalTo: tableFooterViewSuperview.widthAnchor, multiplier: 1.0))
// set tableFooterView height
let height = footerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
tableFooterViewSuperview.addConstraint(footerView.heightAnchor.constraint(equalToConstant: height))
}
}
I think the problem is because you are using autolayout and setting frames to views manually, replace this code:
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.tableView];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
self.tableView.tableHeaderView = self.searchBar;
with something like this:
self.tableView = [UITableView new];
self.tableView.translatesAutoresizingMasksIntoConstraints = NO;
[self.view addSubview:self.tableView];
self.searchBar = [UISearchBar new];
self.searchBar.translatesAutoresizingMasksIntoConstraints = NO;
self.tableView.tableHeaderView = self.searchBar;
It may not work, because maybe searchBar's frame needs to be set manually without constraints.
Here issue regarding space between table view Header and table view Cell. You can handle using Attribute Inspector. Please review that.
- select UITableView
- Under attribute inspector -> Scroll view size -> Content insets, set Top = 44 (or whichever is your nav bar height).
Or you can Handle it programmatically.
- (void)viewDidLoad {
UIEdgeInsets inset = UIEdgeInsetsMake(20, 0, 0, 0);
self.tableView.contentInset = inset;
}
I tried to use this code under in my app delegate in order to add a PNG image as a view controller's background :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[self window] setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]]];
return YES;
}
but I have no luck with it... the view controller still has a white background. What's wrong with that code?
The accepted answer and Michael's answer will work, however, proper way is to use a UIImageView instead. It gives more control over resizing, scaling etc according to different screen sizes on devices. Here is the example;
First create a UIImage.
UIImage *backgroundImage = [UIImage imageNamed:#"iphone_skyline3.jpg"];
Second create a UIImageView. Set the frame size to the parent's (self) frame size. This is important as the frame size will vary on different devices. Stretching will occur depending on the image size. Next assign the image to the view.
UIImageView *backgroundImageView=[[UIImageView alloc]initWithFrame:self.view.frame];
backgroundImageView.image=backgroundImage;
Finally, to keep the image behind all controls do the following. It is important if you are setting the image as a background for your app.
[self.view insertSubview:backgroundImageView atIndex:0];
You need to set the background for your ViewController's view
In your ViewController init or viewDidLoad:
[self.view setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]]];
Here is how it is in swift:
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "background.png"))
}
Swift 4 version of the #hadaytullah answer with some improvements in image adjustments:
override func viewDidLoad() {
super.viewDidLoad()
let backgroundImage = UIImage.init(named: "yourImageNameHere")
let backgroundImageView = UIImageView.init(frame: self.view.frame)
backgroundImageView.image = backgroundImage
backgroundImageView.contentMode = .scaleAspectFill
backgroundImageView.alpha = 0.1
self.view.insertSubview(backgroundImageView, at: 0)
}
You could also write extension to UIViewController to use in multiple places
extension UIViewController {
func setBackgroundImage(imageName: String) {
let backgroundImage = UIImage(named: imageName)
let backgroundImageView = UIImageView(frame: self.view.frame)
backgroundImageView.image = backgroundImage
self.view.insertSubview(backgroundImageView, at: 0)
}
}
In viewDidLoad, I use:
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"yourImageString.png"]];
Alternatively, if you want to do it in appDelegate, I think its possible to
[self.window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"yourImageString.png"]]];
Then in viewDidLoad set background to [UIColor clearColor] ?
If your view controller is having tableView/collection view the below code will suitable for you.
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"yourImage.png"]];
}
you may consider the following solution i use in swift 3.0 and tested in xcode 8
// init and image with your pattern image
var bgUIImage : UIImage = UIImage(named: "yourIamgeName")!
let myInsets : UIEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
bgUIImage = bgUIImage.resizableImage(withCapInsets: myInsets)
self.view.backgroundColor = UIColor.init(patternImage:bgUIImage)
the result will be the following image
You can do it from the storyboard.
Add UIImageView and just set your background image for the Image property in the Attribute Inspector.
There's this cool feature in the UITableViews in Game Center and the search bars they have at their tops. Unlike apps where the search bar is placed in the table header view (so it counts as a standard table cell), instead, it seems to be bolted to the parent navigation bar above it.
So when scrolling the table, the search bar does indeed move, but if you scroll above the boundaries of the table, the search bar never stops touching the navigation bar.
Does anyone know how this might have been done? I was wondering if Apple maybe placed both the search bar and the table in a parent scroll view, but I'm wondering if it may be simpler than that.
Bob's answer is reversed: it ought to be MIN(0, scrollView.contentOffset.y).
Also, in order to properly support resizing (which would occur when rotated), the other frame values should be reused.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UISearchBar *searchBar = searchDisplayController.searchBar;
CGRect rect = searchBar.frame;
rect.origin.y = MIN(0, scrollView.contentOffset.y);
searchBar.frame = rect;
}
You could put the searchBar in the table header and implement the - (void)scrollViewDidScroll:(UIScrollView *)scrollView delegate method for the tableView. Doing something like this should work:
-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
searchBar.frame = CGRectMake(0,MAX(0,scrollView.contentOffset.y),320,44);
}
If you used the searchDisplayController, you would access the searchbar using self.searchDisplayController.searchbar.
In Swift 2.1 and iOS 9.2.1
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
/* Search controller parameters */
searchController.searchResultsUpdater = self // This protocol allows your class to be informed as text changes within the UISearchBar.
searchController.dimsBackgroundDuringPresentation = false // In this instance,using current view to show the results, so do not want to dim current view.
definesPresentationContext = true // ensure that the search bar does not remain on the screen if the user navigates to another view controller while the UISearchController is active.
let tableHeaderView: UIView = UIView.init(frame: searchController.searchBar.frame)
tableHeaderView.addSubview(searchController.searchBar)
self.tableView.tableHeaderView = tableHeaderView
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
let searchBar:UISearchBar = searchController.searchBar
var searchBarFrame:CGRect = searchBar.frame
if searchController.active {
searchBarFrame.origin.y = 10
}
else {
searchBarFrame.origin.y = max(0, scrollView.contentOffset.y + scrollView.contentInset.top)
}
searchController.searchBar.frame = searchBarFrame
}
While other answers seem helpful and partially do the job, it doesn't solve the issue of search bar not receiving the user's touches because it moves outside the bounds of its parent view as you change its frame.
What's worse is that, when you click on the search bar to make it the first responder, it is very likely that the tableView delegate will call tableView:didSelectRowAtIndexPath: on cell that is laid out under the search bar.
In order to address those issues described above, you need to wrap the search bar in a plain UIView, a view which is capable of processing touches occurred outside of its boundaries. By this way, you can relay those touches to the search bar.
So let's do that first:
class SearchBarView: UIView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
for subview in subviews {
if !subview.userInteractionEnabled { continue }
let newPoint = subview.convertPoint(point, fromView: self)
if CGRectContainsPoint(subview.bounds, newPoint) {
return subview.hitTest(newPoint, withEvent: event)
}
}
return super.hitTest(point, withEvent: event)
}
}
Right now, we have a UIView subclass named SearchBarView which is capable of receiving touches occurred outside of its boundaries.
Secondly, we should put the search bar into that new view while the view controller is loading its view:
class TableViewController: UITableViewController {
private let searchBar = UISearchBar(frame: CGRectZero)
...
override func viewDidLoad() {
super.viewDidLoad()
...
searchBar.sizeToFit()
let searchBarView = SearchBarView(frame: searchBar.bounds)
searchBarView.addSubview(searchBar)
tableView.tableHeaderView = searchBarView
}
}
At last, we should update the frame of the search bar as user scrolls down the table view so that it will stay fixed at the top:
override func scrollViewDidScroll(scrollView: UIScrollView) {
searchBar.frame.origin.y = max(0, scrollView.contentOffset.y)
}
Here is the result:
--
Important note: If your table view has sections, they will probably shadow your search bar so you need to bring the search bar on top of them every time the table view's bounds gets updated.
viewDidLayoutSubviews is a good place to do that:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
...
if let tableHeaderView = tableView.tableHeaderView {
tableView.bringSubviewToFront(tableHeaderView)
}
}
--
Hope this helps. You can download the example project from here.
There's one more step if you want to fully emulate the search bars in Game Center.
If you start with friedenberg's excellent answer, as well as followben's modification for iOS 6+ mentioned in the comments, you still need to adjust the functionality when the search bar itself is active.
In Game Center, the search bars will scroll with the table as you scroll down, but will remain fixed below the navigation bar when you attempt to scroll up past the boundaries of the table. However, when the search bar is active and search results are being displayed, the search bar no longer scrolls with the table; it remains fixed in place below the navigation bar.
Here's the complete code (for iOS 6+) for implementing this. (If you're targeting iOS 5 or below, you don't need to wrap the UISearchBar in a UIView)
CustomTableViewController.m
- (void)viewDidLoad
{
UISearchBar *searchBar = [[UISearchBar alloc] init];
...
UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchBar.frame];
[tableHeaderView addSubview:searchBar];
[self.tableView setTableHeaderView:tableHeaderView];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UISearchBar *searchBar = self.tableView.tableHeaderView.subviews.lastObject;
CGRect searchBarFrame = searchBar.frame;
/*
* In your UISearchBarDelegate implementation, set a boolean flag when
* searchBarTextDidBeginEditing (true) and searchBarTextDidEndEditing (false)
* are called.
*/
if (self.inSearchMode)
{
searchBarFrame.origin.y = scrollView.contentOffset.y;
}
else
{
searchBarFrame.origin.y = MIN(0, scrollView.contentOffset.y);
}
searchBar.frame = searchBarFrame;
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
self.inSearchMode = YES;
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
self.inSearchMode = NO;
}
Voilà ! Now, when the search bar is inactive it will move with the table, and remain fixed when attempting to move beyond the table boundaries. When active, it will remain fixed in place, just like in Game Center.
All of the other answers here provided me with helpful information, but none of them worked using iOS 7.1. Here's a simplified version of what worked for me:
MyViewController.h:
#interface MyViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate> {
}
#end
MyViewController.m:
#implementation MyViewController {
UITableView *tableView;
UISearchDisplayController *searchDisplayController;
BOOL isSearching;
}
-(void)viewDidLoad {
[super viewDidLoad];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
searchBar.delegate = self;
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
searchDisplayController.searchResultsDelegate = self;
UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchDisplayController.searchBar.frame];
[tableHeaderView addSubview:searchDisplayController.searchBar];
[tableView setTableHeaderView:tableHeaderView];
isSearching = NO;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
UISearchBar *searchBar = searchDisplayController.searchBar;
CGRect searchBarFrame = searchBar.frame;
if (isSearching) {
searchBarFrame.origin.y = 0;
} else {
searchBarFrame.origin.y = MAX(0, scrollView.contentOffset.y + scrollView.contentInset.top);
}
searchDisplayController.searchBar.frame = searchBarFrame;
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
isSearching = YES;
}
-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
isSearching = NO;
}
#end
Note: If you're using "pull down to refresh" on your list, you'll need to replace scrollView.contentInset.top in scrollViewDidScroll: with a constant to allow the search bar to scroll over the refresh animation.
If your deployment target is iOS 9 and higher then you can use anchors and set UISearchBar and UITableView programmatically:
private let tableView = UITableView(frame: .zero, style: .plain)
private let searchBar = UISearchBar(frame: CGRect .zero)
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.contentInset = UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0)
searchBar.delegate = self
view.addSubview(tableView)
view.addSubview(searchBar)
NSLayoutConstraint.activate([
searchBar.heightAnchor.constraint(equalToConstant: 44.0),
searchBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
searchBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
searchBar.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
])
}
I assume that you create UISearchBar and UITableView from code, not in storyboard.