#IBAction func searchOn() {
self.searchController.searchResultsUpdater = self
self.searchController.hidesNavigationBarDuringPresentation = true
self.searchController.dimsBackgroundDuringPresentation = false
self.definesPresentationContext = true
self.present(searchController, animated: true, completion: nil)
}
What I want to do here is to hide navigation bar when presenting Search bar and place cells in correct position, because now they are going under search bar.
One of the solutions I was able to think of is just to animateWithDuration UIEdgeInsetsMake downwards same distance as they get moved up when navigation bar hides. But the problem is that I don't know the duration of the animation between switching navigation and search bars.
Can anyone help figure this out?
Or at least help me to get the duration of animation which switches between navigation and search bars.
write this code in viewDidLoad() method.
let flow: UICollectionViewFlowLayout = CollectionView.collectionViewLayout as! UICollectionViewFlowLayout
flow.sectionInset = UIEdgeInsetsMake(70, 0, 0, 0)
one more way is to try setting the contentOffset to required position.and change the y position to move up and down, u can give negative values also
for example,
//without animation
collectionview.contentOffset = CGPoint(x: 0, y: 100);
//with animation
collectionview.setContentOffset(CGPoint(x: 0, y: 100), animated: true)
and also u can take advantage of sectionInset also as Chirag Patel mentioned
Related
This should be an easy question for most of you. Presenting view controllers like in the attached photo now have a bar at the top of them (see red arrow) to indicate that the user can swipe down to dismiss the controller. Please help with any of the following questions:
What is the proper term for this icon?
Is it part of swift's ui tools / library or is it just a UIImage?
Can someone provide a simple snippet on how to implement - perhaps it is something similar to the code below
let sampleController = SampleController()
sampleController.POSSIBLE_OPTION_TO_SHOW_BAR_ICON = true
present(sampleController, animated: true, completion: nil)
Please see the red arrow for the icon that I am referring to
grabber
grabber is a small horizontal indicator that can appear at the top edge of a
sheet.
In general, include a grabber in a resizable sheet. A grabber shows people that they can drag the sheet to resize it; they can also
tap it to cycle through the detents. In addition to providing a visual
indicator of resizability, a grabber also works with VoiceOver so
people can resize the sheet without seeing the screen. For developer
guidance, see prefersGrabberVisible.
https://developer.apple.com/design/human-interface-guidelines/ios/views/sheets/
From iOS 15+ UISheetPresentationController has property prefersGrabberVisible
https://developer.apple.com/documentation/uikit/uisheetpresentationcontroller/3801906-prefersgrabbervisible
A grabber is a visual affordance that indicates that a sheet is
resizable. Showing a grabber may be useful when it isn't apparent that
a sheet can resize or when the sheet can't dismiss interactively.
Set this value to true for the system to draw a grabber in the
standard system-defined location. The system automatically hides the
grabber at appropriate times, like when the sheet is full screen in a
compact-height size class or when another sheet presents on top of it.
Playground snippet for iOS 15:
import UIKit
import PlaygroundSupport
let viewController = UIViewController()
viewController.view.frame = CGRect(x: 0, y: 0, width: 380, height: 800)
viewController.view.backgroundColor = .white
PlaygroundPage.current.liveView = viewController.view
PlaygroundPage.current.needsIndefiniteExecution = true
let button = UIButton(primaryAction: UIAction { _ in showModal() })
button.setTitle("Show page sheet", for: .normal)
viewController.view.addSubview(button)
button.frame = CGRect(x: 90, y: 100, width: 200, height: 44)
func showModal {
let viewControllerToPresent = UIViewController()
viewControllerToPresent.view.backgroundColor = .blue.withAlphaComponent(0.5)
viewControllerToPresent.modalPresentationStyle = .pageSheet // or .formSheet
if let sheet = viewControllerToPresent.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersGrabberVisible = true
}
viewController.present(viewControllerToPresent, animated: true, completion: nil)
}
The feature you are asking is not available in UIKit.
You have to implement custom view-controller transition animation with subclassing UIPresentationController for rendering that pull up/down handle.
UIPresentationController (developer.apple.com)
For custom presentations, you can provide your own presentation controller to give the presented view controller a custom appearance. Presentation controllers manage any custom chrome that is separate from the view controller and its contents. For example, a dimming view placed behind the view controller’s view would be managed by a presentation controller. Apple Documentation
This can be achieved by any UIView or you can use any image if you want by adding subview to UIPresentationController's contentView above the presentedView.
To provide the swipe gesture to dismiss/present, you must implement UIPercentDrivenInteractionController.
You can refer to this tutorial below for detailed understanding.
UIPresentationController Tutorial By raywenderlich.com
You should look for presentationDirection = .bottom in your case.
For gesture driven dismissal, you should check below tutorial
Custom-UIViewcontroller-Transitions-getting-started
I hope this might help you.
If you need to add this indicator within the view controller that is being presented if you do not want to do any custom presentations and just work with the default transitions.
The first thing to think about is your view hierarchy, is the indicator going to be part of your navigation bar or perhaps your view does not have navigation bar - so accordingly you probably need some code to find the correct view to add this indicator to.
In my scenario, I needed a navigation bar so my view controllers were within a navigation controller but you could do the same inside your view controllers directly:
1: Subclass a Navigation Controller
This is optional but it would be nice to abstract away all of this customization into the navigation controller.
I do a check to see if the NavigationController is being presented. This might not be the best way to check but since this is not part of the question, refer to these answers to check if a view controller was presented modally or not
class CustomNavigationController: UINavigationController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// this checks if the ViewController is being presented
if presentingViewController != nil {
addModalIndicator()
}
}
private func addModalIndicator() {
let indicator = UIView()
indicator.backgroundColor = .tertiaryLabel
let indicatorSize = CGSize(width: 30, height: 5)
let indicatorX = (navigationBar.frame.width - indicatorSize.width) / CGFloat(2)
indicator.frame = CGRect(origin: CGPoint(x: indicatorX, y: 8), size: indicatorSize)
indicator.layer.cornerRadius = indicatorSize.height / CGFloat(2.0)
navigationBar.addSubview(indicator)
}
}
2: Present the Custom Navigation Controller
let someVC = UIViewController()
let customNavigationController = CustomNavigationController()
customNavigationController.setViewControllers([stationsVC], animated: false)
present(playerNavigationController, animated: true) { }
3: This will produce the following results
You might need to alter some logic here based on your scenario / view controller hierarchy but hopefully this gives you some ideas.
How make animation like ios default mail. I need the same effect like search bar is hide at initial and when i drag table view download the it shows the search bar.
like in the screenshot
update to the question.- Right now i am using a searchBar above tableview in the view controller.
var resultSearchController = UISearchController()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
workActivityTableView.tableHeaderView = controller.searchBar
workActivityTableView.contentOffset = CGPoint(x: 0, y: controller.searchBar.frame.height)
navigationController?.extendedLayoutIncludesOpaqueBars = true
return controller
})()
Drag search bar in to your tableview in your storyboard as it should be the first subview of your tableview!
Tableview will consider it as a header.
Your view hierarchy will be look like,
And the default size of search bar will be 44.
Now in your viewDidload set content offset of your table view like,
_tblView.contentOffset = CGPointMake(0, 44);
or in swift you can say,
tblView.contentOffset = CGPoint(x: 0, y: 44)
and you're done!
bellows are screenshot of results,
Initially
After scroll
This will not work if prefersLargeTitles set to true then initially you will get search bar!
I'd suggest you add the search bar as a tableHeaderView to your tableView, and in your viewDidLoad, add the following lines:
tableView.tableHeaderView = your_searchbar
tableView.contentOffset = CGPoint(x: 0, y: your_searchar.frame.height)
navigationController?.extendedLayoutIncludesOpaqueBars = true
UPDATE:
This can be easily achieved by adding the search controller as a part of navigationItem. Something like this:
self.navigationItem.searchController = searchController
This is available from iOS 11.
I want navigationBar to overlap the root view of view controller, I have tried two methods:
self.additionalSafeAreaInsets = UIEdgeInsetsMake(-100, 0, 0, 0);
and
self.edgesForExtendedLayout = UIRectEdgeAll;
self.extendedLayoutIncludesOpaqueBars = YES;
Neither of these options work.
So at the moment it looks like the figure below (The gradient orange is the navigationBar)View content below the navigationBar
However what I'd like to achieve is that the actual content of the viewController starts at the top of the screen and the gradient orange overlap the viewController.
If I still can not express it clearly, please tell me. Thank you.
Not sure what u meant but maybe:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(false, animated: false)
}
Or set it visible from the Storyboard
I have a custom UITableView with custom cells (70px height for each cell).
I have also a 49px UITabBar, but it's hides the tableView.
I've tried to add this line to the TableView awakeFromNib method but it didn't work:
self.commentsTableView.contentInset = UIEdgeInsetsMake(0, 0, 49, 0)
Any idea how can I solve this?
Thanks!
i don't know what you did exactly, but try like this:
self.edgesForExtendedLayout = UIRectEdgeAll;
self.tableview.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
I hope, this will work.
I ran into this issue when dealing with a table view in a navigation controller that did not have translucent bars. I performed a setup similar to the following:
override func viewDidLoad() {
super.viewDidLoad()
// Without this there is some extra fast inertia when slowly
// scrolling to the top.
extendedLayoutIncludesOpaqueBars = true
// Don't extend the tableview past the bottom bar, though.
// If we do then a tab bar or bottom nav bar will block
// content.
edgesForExtendedLayout = [.top, .left, .right]
}
However, I later discovered that a couple of checkboxes were unchecked in a storyboard higher up the hierarchy. Specifically these two:
Checking these two boxes removed the need to care about the content insets and the layout extending behavior in that view controller
This did the trick for me in Swift 3.
if let tabBarController = tabBarController {
self.tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, tabBarController.tabBar.frame.height, 0.0);
}
You should config the corresponding view controller with following code to remove the edges extend (It defaults to UIRectEdgeAll)
edgesForExtendedLayout = []
Try this
self.commentsTableView.contentInset = UIEdgeInsetsMake(49, 0, 0, 0)
self.commentsTableView.setContentOffset(CGPoint.init(x: 0, y: -49), animated: false)
If you changed self.hidesBottomBarWhenPushed=true in previous view controller, then make sure to change it to self.hidesBottomBarWhenPushed=false inside override func prepare(for segue: UIStoryboardSegue, sender: Any?) {}
Try to use constraints for tableView and TabBar like:
I have a UINavigationController which contains a UITableViewController. This navigation controller pushes other UITableViewControllers around and eventually these table view controllers will have a prompt.
The problem is when I set this prompt programmatically it overlaps the content of the table view underneath it.
(A search bar is being hidden by the navigation bar)
I was looking around in SO and found this answer. I tried the suggestion there in two different ways in the affected view controller but nothing changed:
override func viewDidLoad() {
super.viewDidLoad()
self.edgesForExtendedLayout = .None;
self.extendedLayoutIncludesOpaqueBars = false;
self.navigationItem.title = NSLocalizedString("Add Anime or Manga", comment: "")
self.navigationItem.prompt = NSLocalizedString("Search media belonging to this series.", comment: "")
}
-
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = NSLocalizedString("Add Anime or Manga", comment: "")
self.navigationItem.prompt = NSLocalizedString("Search media belonging to this series.", comment: "")
self.edgesForExtendedLayout = .None;
self.extendedLayoutIncludesOpaqueBars = false;
}
A comment in that same answer linked to this Apple guide on preventing views from overlapping each other. The problem is UITableViewController doesn't appear to have top/bottom layout guides so I can't create a constraint (another SO answer says having said layouts in table view controllers is irrelevant).
As such I have exhausted all my options.
I have tried to reproduce your problem and it seems that when not all the viewControllers have a prompt the navigationBar is somehow not resizing properly.
It seems you need to somehow trigger the layouting for the UINavigationController. The only way I could make it work properly was by adding this in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES];
[self.navigationController setNavigationBarHidden:NO];
}
Maybe this prompt is meant to be used consistently across the entire application (meaning having one for all viewControllers or none of them), that's why the UINavigationController does not layout it's subviews when it changes.
Hope this works for you too.
Select your TableViewController from document outline and change the value to translucent navigation bar of top bar in attributes inspector. Be sure that you will not select uitableview you should select your your table view controller(aka File's Owner) from document outline.
You have to set prompt only if view did appear, then it works:
override func viewDidAppear(_ animated: Bool) {
navigationItem.prompt = "your prompt here"
}
It's 2019 and this is still not fixed. Slow Clap. I refuse to be cowed by such things so I hammered iOS into submission with the dirtiest trick in the book. I fixed this by doing a disgusting -44 "y trick" on the UINavigation while placing the UITableView in top 44, I know it's stupid, but it works.. I am sure new fangled phones will ruin my genius.. but hey ho.. I have lazily left irrelevant code (because I am idle) but hopefully you can see what I did.
WITHOUT THE y: -44 Hack
WITH THE y: -44 Hack
let screenSize: CGRect = UIScreen.main.bounds
let navBar = UINavigationBar(frame: CGRect(x: 0, y: -44, width: screenSize.width, height: 44)) //<<--note minus 44
navBar.barTintColor = Globals.Color_BackgroundGrey()
navBar.isTranslucent = false
tableView.contentInset = UIEdgeInsets(top: 44, left: 0, bottom: 0, right: 0); //<--note plus 44
self.edgesForExtendedLayout = []
let navItem = UINavigationItem(title: "Boaty Mc Boatface")
let doneItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(done))
navItem.rightBarButtonItem = doneItem
navBar.setItems([navItem], animated: false)
view.addSubview(navBar)