ScrollView not working after reload view swift 3 - ios

I'd like to reload my View but after I reload it my ScrollView not working, but everything else refresh correctly. Here is my code to refresh view:
extension UIViewController {
func reloadViewFromNib() {
let parent = view.superview
view.removeFromSuperview()
view = nil
parent?.addSubview(view) // This line causes the view to be reloaded
}
}
I tried to put these into my viewDidLoad maybe I just need to refresh the SrollView manually but it's still not working.
self.beSrcollView.bounces = true
self.beSrcollView.delegate = self
self.beSrcollView.updateConstraints()
self.beSrcollView.isScrollEnabled = true
self.beSrcollView.updateFocusIfNeeded()
self.beSrcollView.setNeedsFocusUpdate()
self.beSrcollView.needsUpdateConstraints()
Have you any idea how to fix this?

override func viewDidLayoutSubviews()
{
self.beSrcollView.contentSize = CGSize(width: self.view.frame.size.width, height: 1000) // height change according
}

Try This
scrollView.reloadInputViews()

Related

Adding a UIView/UIGestureRecognizer to the presented view in a UIPresentationController

I am trying to recreate the bottom drawer functionality seen in Maps or Siri Shortcuts by using a UIPresentationController by having it recognise user input and updating the frameOfPresentedViewInContainerView accordingly. However I want this mechanism to work independently of the presented UIViewController as much as possible so I'm trying to have the presentation controller add a handle area above the view. Ideally the view of the presented controller and the handle are should both recognise user input.
This works for the presented view, however any view I add to it responds to no UIGestureRecognizer at all. Am I missing something?
class PresentationController: UIPresentationController {
private let handleArea: UIView = UIView()
override var frameOfPresentedViewInContainerView: CGRect {
// Return some frame for now
return CGRect(x: 0, y: 250, width: containerView!.frame.width, height: 500)
}
override func presentationTransitionWillBegin() {
// Unwrap presented view
guard let presentedView = self.presentedView else {
return
}
// Set color
self.handleArea.backgroundColor = UIColor.green
// Add to view hierachy
presentedView.addSubview(self.handleArea)
// Set constraints
self.handleArea.trailingAnchor.constraint(equalTo: presentedView.trailingAnchor).isActive = true
self.handleArea.leadingAnchor.constraint(equalTo: presentedView.leadingAnchor).isActive = true
self.handleArea.bottomAnchor.constraint(equalTo: presentedView.topAnchor).isActive = true
self.handleArea.heightAnchor.constraint(equalToConstant: 56).isActive = true
self.handleArea.translatesAutoresizingMaskIntoConstraints = false
// These don't help
self.handleArea.isUserInteractionEnabled = true
presentedView.isUserInteractionEnabled = true
presentedView.bringSubviewToFront(self.handleArea)
}
override func presentationTransitionDidEnd(_ completed: Bool) {
if completed {
// Add gesture recognizer
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.onHandleAreaTapped(sender:)))
self.handleArea.addGestureRecognizer(tapGestureRecognizer)
}
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
// Remove subview
self.handleArea.removeFromSuperview()
}
// MARK: - Responder
#objc private func onHandleAreaTapped(sender: UITapGestureRecognizer) {
print("tap") // No output
}
}
I managed to solve it by adding both the handle area and the view of the presentedViewController to a custom view and then overriding the presentedView property and returning my custom view.

Having trouble with LargeTitle and a segmented control with a table view

Sample project can be found at https://github.com/SRowley90/LargeTitleIssueTestiOS
I am trying to position a segmented control below the Large title in an iOS app. I have a UIToolbar which contains the segmented control inside.
When scrolling up the title and toolbar behave as expected.
When scrolling down the navigation bar is correct, but it doesn't push the UITabBar or the UITableView down, meaning the title goes above the segmented control as can be seen in the images below.
I'm pretty sure it's something to do with the constraints I have set, but I can't figure out what.
The TabBar is fixed to the top, left and right.
The TableView is fixed to the bottom, left and right.
The tableView is fixed vertically to the TabBar
I have the position UITabBarDelegate method set:
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
Take the delegation of the tableView somewhere:
tableView.delegate = self
Override the scrollViewDidScroll and update toolbar position appearance (since the real position should not change according to have that nice bounce effect.
extension ViewController: UIScrollViewDelegate {
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
var verticalOffset = scrollView.contentOffset.y + defaultNavigationBarHeight
if scrollView.refreshControl?.isRefreshing ?? false {
verticalOffset += 60 // After is refreshing changes its value the toolbar goes 60 points down
print(toolbar.frame.origin.y)
}
if verticalOffset >= 0 {
toolbar.transform = .identity
} else {
toolbar.transform = CGAffineTransform(translationX: 0, y: -verticalOffset)
}
}
}
You can use the following check before applying transformation to make it more reliable and natural to default iOS style:
if #available(iOS 11.0, *) {
guard let navigationController = navigationController else { return }
guard navigationController.navigationBar.prefersLargeTitles else { return }
guard navigationController.navigationItem.largeTitleDisplayMode != .never else { return }
}
Using UIScrollViewDelegate didn't work well with CollectionView and toolbar for me. So, I did:
final class CollectionViewController: UICollectionViewController {
private var observesBag: [NSKeyValueObservation] = []
private let toolbar = UIToolbar()
override func viewDidLoad() {
super.viewDidLoad()
let statusBarHeight = UIApplication.shared.statusBarFrame.height
let navigationBarHeight = navigationController?.navigationBar.frame.height ?? 0
let defaultNavigationBarHeight = statusBarHeight + navigationBarHeight
let observation = navigationController!
.navigationBar
.observe(\.center, options: NSKeyValueObservingOptions.new) { [weak self] navBar, _ in
guard let self = self else { return }
let newNavigatonBarHeight = navBar.frame.height + statusBarHeight
let yTranslantion = newNavigatonBarHeight - defaultNavigationBarHeight
if yTranslantion > 0 {
self.toolbar.transform = CGAffineTransform(
translationX: 0,
y: yTranslantion
)
} else {
self.toolbar.transform = .identity
}
}
observesBag.append(observation)
}
}
Observe the "center" of the navigationBar for changes and then translate the toolbar in the y-axis.
Even though it worked fine when I tried to use this solution with UIRefreshControl and Large Titles it didn't work well.
I set up the refresh control like:
private func setupRefreshControl() {
let refreshControl = UIRefreshControl()
self.webView.scrollView.refreshControl = refreshControl
}
the height of the UINavigationBar is changed after the complete refresh triggers.

hide custom search bar on start

in my swift 2 app i have a table view controller
where i added a custom search bar (dark grey row)
in the viewDidLoad i call
configureCustomSearchController()
this is the function:
func configureCustomSearchController() {
customSearchController = CustomSearchController(
searchResultsController: self,
searchBarFrame: CGRectMake(0.0, 0.0, MyTable.frame.size.width, 50.0),
MyTable.tableHeaderView = customSearchController.customSearchBar
customSearchController.searchBar.sizeToFit()
customSearchController.customDelegate = self
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
// DO SOMETHING
}
func didStartSearching() {
// DO SOMETHING
}
func didTapOnSearchButton() {
// DO SOMETHING
}
func didTapOnCancelButton() {
// DO SOMETHING
}
func didChangeSearchText(searchText: String) {
// DO SOMETHING
}
My Question is, how can i hide the searcher, if my tableview will appear and show the sidebar if i pull down.
a pull down refresh function is available.
i found my solution:
viewDidLoad
MYTable.contentInset = UIEdgeInsets(top: -1, left: 0, bottom: 0, right: 0)
this hide the searchbar on start and if i scroll a little bit up, the search Bar will be visible
if you want to show searchbar when user scroll down then add this Code
self.MyTable.tableHeaderView = customSearchController
self.MyTable.contentOffset = CGPointMake(0, CGRectGetHeight(customSearchController.frame))
customSearchController.delegate = self
If you want to hide the header which contains your custom search bar you can try the following solutions:
When you have to hide header (At start):
tableview.contentOffset = CGPointMake(0,44)
When you have to show header (On the pull down action ):
tableview.contentOffset = CGPointMake(0,0)
Ref:https://stackoverflow.com/a/14433304/4557505
Another option is
table.tableHeaderView = nil
And later if you want to show it then just assign it again,
table.tableHeaderView = theHeaderView;
You can find more information about this on https://stackoverflow.com/a/8079933/4557505
Hope this may help you
Else you may set your search bar on the viewController's view and show/hide that based on your pull to refresh

view.traitCollection.horizontalSizeClass returning undefined (0) in viewDidLoad

I'm using a UITextView inside a UIPageViewController, and I want to determine the font size based on the size class of the device.
The first slide of the page view is loaded in ViewDidLoad like so (viewControllerAtIndex(0)):
override func viewDidLoad() {
super.viewDidLoad()
//Some unrelated code here
// Page View Controller for Questions Slider
questionPageVC = storyboard?.instantiateViewControllerWithIdentifier("QuestionPageView") as? UIPageViewController
questionPageVC!.dataSource = self;
questionPageVC!.delegate = self;
let startingViewController : QuestionContentViewController = viewControllerAtIndex(0) as QuestionContentViewController
var viewControllers = [startingViewController]
questionPageVC!.setViewControllers(viewControllers, direction: .Forward, animated: true, completion: nil)
let sliderHeight = view.frame.size.height * 0.5
questionPageVC!.view.frame = CGRectMake(20, 70,
view.frame.size.width-40, sliderHeight)
addChildViewController(questionPageVC!)
view.addSubview(questionPageVC!.view!)
questionPageVC?.didMoveToParentViewController(self)
var pageControl : UIPageControl = UIPageControl.appearance()
pageControl.pageIndicatorTintColor = UIColor.lightGrayColor()
pageControl.currentPageIndicatorTintColor = UIColor.blackColor()
pageControl.backgroundColor = UIColor.whiteColor()
// Some more code here
}
And then, in viewControllerAtIndex:
private func viewControllerAtIndex(index: Int) -> QuestionContentViewController {
var pcvc : QuestionContentViewController = storyboard?.instantiateViewControllerWithIdentifier("QuestionContentView") as! QuestionContentViewController
var fontSize = ""
if (view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact) {
fontSize = "20"
} else {
fontSize = "28"
}
pcvc.questionString = TextFormatter(string: fontSize + questionsArray[index]).formattedString
pcvc.questionIndex = index
return pcvc
}
The problem is that the very first slide, which was called in viewDidLoad, always uses the font size in the "else" clause.
If I print view.traitCollection.horizontalSizeClass, for that first slide, I get 0 (UIUserInterfaceSizeClassUnspecified), for subsequent slides, I get the correct size.
I tried moving the whole thing to "viewWillAppear", and then weird things happen to the UIPageViewController (an extra slide with the wrong size text behind the other slides)
The problem is that viewDidLoad is too soon to be asking about a view's trait collection. This is because the trait collection of a view is a feature acquired from the view hierarchy, the environment in which it finds itself. But in viewDidLoad, the view has no environment: it is not in in the view hierarchy yet. It has loaded, meaning that it exists: the view controller now has a view. But it has not been put into the interface yet, and it will not be put into the interface until viewDidAppear:, which comes later in the sequence of events.
However, the view controller also has a trait collection, and it does have an environment: by the time viewDidLoad is called, the view controller is part of the view controller hierarchy. Therefore the simplest (and correct) solution is to ask for the traitCollection of self, not of view. Just say self.traitCollection where you now have view.traitCollection, and all will be well.
(Your solution, asking the screen for its trait collection, may happen to work, but it is not reliable and is not the correct approach. This is because it is possible for the parent view controller to alter the trait collection of its child, and if you bypass the correct approach and ask the screen, directly, you will fail to get the correct trait collection.)
I found that if I use the main screen's traitCollection, instead of the current view, I get the correct size class:
if (UIScreen.main.traitCollection.horizontalSizeClass == .compact) {
fontSize = "20"
} else {
fontSize = "28"
}
You will be better off moving that code to the viewWillAppear method, as in the viewDidLoad the ViewController's view has not been added to the hierarchy yet, and you might get an empty trait collection.
In my case I needed my view to know about the horizontalSizeClass so accessing the UIScreen traitCollection was tempting but not encouraged, so I had something like this:
override func layoutSubviews() {
super.layoutSubviews()
print("\(self.traitCollection.horizontalSizeClass.rawValue)")
switch self.traitCollection.horizontalSizeClass {
case .regular, .unspecified:
fontSize = 28
case .compact:
fontSize = 20
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
print("\(self.traitCollection.horizontalSizeClass.rawValue)")
guard let previousTraitCollection = previousTraitCollection else { return }
if self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass {
layoutIfNeeded()
}
}
I took a hint from this Apple Technical Q&A and used viewWillLayoutSubviews instead of viewDidLoad/viewWillAppear.

Adding View Programmatically from ViewController super class doesn't show in front

I have a BaseViewController that my UIViewControllers extend so i can have explicit functions that i dont need to rewrite. Something i would like would be a functions such as self.showSpinner() and the viewController would show the spinner
My Code looks like this
class BaseViewController: UIViewController {
var actvIndicator : UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
self.actvIndicator = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
self.actvIndicator.color = UIColor.blackColor()
self.actvIndicator.backgroundColor = UIColor.blackColor()
self.actvIndicator.frame = CGRectMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2, 100, 100);
self.actvIndicator.center = self.view.center
self.actvIndicator .startAnimating()
self.view.addSubview(self.actvIndicator)
self.actvIndicator.bringSubviewToFront(self.view)
self.edgesForExtendedLayout = UIRectEdge.None
self.navigationController?.navigationBar.translucent = false
}
func showSpinner(){
self.actvIndicator.startAnimating()
}
func hideSpinner(){
self.actvIndicator.stopAnimating()
}
}
And my viewcontrollers looks like this
class MyProjectViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.showSpinner()
}
}
MyProjectViewController have UITableView that fills the entire screen. When i set tblProjects.alpha = 0 i can see the spinner. But i want it in the front.
i also tried self.view.bringSubviewToFront(self.actvIndicator)
What am i missing?
A couple quick notes before I get into what I think your problem is:
When you add a subview it is automatically added to the top layer, no need for the bringSubviewToFront: in viewDidLoad: (which is being used wrong anyway).
You should not set view frames in viewDidLoad: (e.g. centering a view). Frames are not setup yet, so you should move that to viewWillAppear: or some other variant.
Now your issue is most likely a view hierarchy problem (further confirmed by your comment) and thus can probably be fixed by pushing the spinner to the front every time you want it to be shown, like:
func showSpinner() {
self.view.bringSubviewToFront(self.actvIndicator)
self.actvIndicator.startAnimating()
}
The problem here stands on the fact that table view is draw after you are calling self.view.bringSubviewToFront(self.actvIndicator). A possible workaround for this is to call bringSubviewToFront when showing the spinner
func showSpinner(){
self.view.bringSubviewToFront(self.actvIndicator)
self.actvIndicator.startAnimating()
}

Resources