Scrolling a UIScrollView regardless of where the touch occurs - ios

The end goal is to create the same scrolling effect as the one on the "Me" tab of the twitter iOS app where the segmented control rises to the top as you scroll down and then stays fixed to it unless scrolled back up to the top.
The solution I've come up with is illustrated below. There is a view at the top, a segmented control, and a table view beneath the segmented control. All of these elements are embedded inside a scrollview that takes up the entire screen (minus tab & nav bars).
Here is the key issue: If begin scrolling by swiping up from the top-most view or the segmented control, it scrolls the scrollview that all the elements are embedded in. If I scroll the tableview, it will only scroll itself and leave the top-most view and segmented control unaffected.
How can I scroll the scrollview that the elements are embedded in no matter where the scrolling occurs on screen?

I had a similar layout in one of my projects. I used SJSegmentedViewController.
It Requires a headerViewController ,datasource for middle segment and viewController array for those segments.
This library allows you to scroll from anywhere on the screen moreover the segmented control sticks to the top as user scrolls all the way to top.
Here is how you can implement this :
First import the module into your class
import SJSegmentedScrollView
Then create a headerViewController and two viewControllers(Say Video and Tips) for segment
let headerViewController = HeaderViewController()
let video = VideoController()
let tips = TipsController()
After that set these Controller and also set the title for segmented control as following:
segmentController.headerViewController = header
segmentController.segmentControllers = [video,tips]
video.title = "Video"
tips.title = "Tips"
Then add it to the Container View
addChildViewController(segmentController)
containerView.addSubview(segmentController.view)
segmentController.view.frame = self.containerView.bounds
segmentController.didMove(toParentViewController: self)
Here Container View is a UIContainerView
Last but make sure to call in child controllers (VideoController,TipsController), After calling this function in those controllers you can scroll from anywhere on the screen.
extension HomeListingViewController: SJSegmentedViewControllerViewSource {
func viewForSegmentControllerToObserveContentOffsetChange() -> UIView {
//Scrollview in child controllers
return scrollview
}
}
You can find the full documentation here
Hope this helps!

Related

iOS Personal Hotspot breaks layout when navigation bar is hidden

I'm writing an application for iOS with Swift and I'm using the auto layout in all my View Controllers but When the personal Hotspot is activated, the view doesn't resize correctly and The bottom of the view goes below the screen. I found It doesn't happen to my all views, except in the views that I have this line of code:
navigationController?.navigationBar.isHidden = true
How can I handle this situation?
I found a solution. I always have this problem when I add a child view controller and the bottom of the child view goes below the screen. I found when the blue bar appears, the height of my parent view controller will be lower than the device screen's height. So I need to change the child view position in the situation.
if let parentHeight = parent?.view.frame.height, parentHeight < UIScreen.main.bounds.height {
view.frame.origin.y = UIScreen.main.bounds.height-childViewHeight-8
}

How to get TouchesBegan working on a subView (Child view) rather than on Parent view?

I have a setup like this
Scroll View (Main View) -> CanvasView (SubView) -> Pages (SubView of
Canvas).
I want to add Touches began functionality to Pages , but for it to be detected I had to setUserInteractionEnabled = YES; to every parent view.
Now when I try to look for the view which is being detected I get the view of class UIScrollView.
As you can see in the image I have an UISCrollview on which a black screen whose subview is the white view. I need to do the touches began on the white view.

iOS 11 on NavigationBar pull down - height of bar changes?

What I want to to: I want to drag down the whole view of a viewController to dismiss to the parent viewController using a pan gesture recognizer.
The Problem: When I drag the view down, the navigationBar decreases its height and does not look good. When the view returns to its original position, the navigationBar returns to the default size. I want the navigationBar to stay at its size. I also tried to use the new large titles and some other properties of the navigationController/-bar, but that did not solve it.
Note: Everything worked fine already before iOS 11.
My code:
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(dragViewDown(_:)))
navigationController!.view.addGestureRecognizer(panGesture)
}
#IBAction func dragViewDown(_ gesture: UIPanGestureRecognizer) {
if let dragView = gesture.view {
let translation = gesture.translation(in: dragView)
dragView.center.y = (dragView.center.y + translation.y)
gesture.setTranslation(CGPoint.zero, in: dragView)
}
}
This test project only has one viewController and does not provide the dismissal, but the problem is the same as in my working project.
I also uploaded the project to GitHub: https://github.com/maddinK7/navitationBar-pull-down-problem
Does anyone have an idea how to solve this? Thanks in advance.
I want the navigationBar to stay at its size
It is staying at its size. If you check the navigation bar's bounds size height before, during, and after the drag, you will see that it remains the same (probably 44) at all times. What's changing is the drawing extension that causes the drawing of the nav bar to extend up behind the status bar. It can't do that when you pull the whole thing away from the top of the screen, because it is not at the top next to the status bar any more. iOS 11 is more strict about the way it performs this drawing extension, probably because it has to do it in a special way on the iPhone X.
So, let's make sure you're doing this correctly:
Make sure that the navigation bar has a top constraint pinned to the safe area layout guide's top, with a constant of zero.
Make sure that the navigation bar has a delegate that returns .topAttached from position(forBar:).
If you are doing both those things and it doesn't help, you'll have to implement this in some other way entirely. Making the view directly draggable like this, without a custom parent view controller, was always dubious.
When UINavigationController attached top, system will add safe area top margin in the navigation background.
(NOTICE: Background margin will not changed when offset value is between 1 and 0)
So you have to handle attached/detached top event by handle gesture offset to change the right offset and content insets.
You can try the solution in my lib example. ;)
My example include UITableViewController in the UINavigationController, so it will relatively complex.
https://github.com/showang/OverlayModalViewController

Attaching UIButton on top of UIScrollView or UITableView

What is the best approach for attaching a UIButton on top of UIScrollView or UITableView so when the view is scrolled, the button stays in its place.
Here examples below:
UIButton stays in the right bottom corner when the view is scrolled.
google+ app example
yahoo mail app example
I think this should work. Lay Out your button in a view that is outside of the tableviewcontroller. Then drag an outlet to the tableviewcontroller file. Then add it in code. This code would hold it at the top of the screen.
#IBOutlet var buttonView: UIView!
override func viewDidLayoutSubviews() {
self.view.addSubview(buttonView)
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
var rect = self.buttonView.frame
rect.origin.y = max(0,scrollView.contentOffset.y + scrollView.contentInset.top)
self.buttonView.frame = rect
}
Thank you all for great answers!
I got it worked through storyboard by moving the button from scrollView to View itself. That way it's attached on UIView and it's independent of scrollview.
storyboard snapshot
So now the structure is:
- View
- ScrollView
- Button
Before it was:
- View
- ScrollView
- Button
There are many ways to go about doing this but two that I use most often are as follows.
One approach is embedding the view controller within a navigation controller. This will set a bar on the top and bottom if you choose that you can place bar button items upon.
Another approach is to place a UIView along the top and snap the constraints to the left, right, and top with 0 no-margin. Then set the height. I usually use 40px for the height but you can use what is applicable to your needs. After that you can place a button in that UIView and then set constraints on it to keep in in place.
In my experience, this isn't reliably possible to do with the scrollView itself.
My solution is usually to put anything that needs to float above the tableView/scrollView in a plain ViewController that also contains the tableView/scrollView parent.
If you're using storyboards with a UITableViewController scene, this will likely mean you need to use another scene with UIViewController with a container that has your UITableViewController.
For UITableView use tableHeaderView. For UIScrollView you need to create a separate view not in the scroll view's hierarchy.
Another solution is to put your UIButton in a UIToolbar, and then make the toolbar a child of the UINavigationController's view. After that, in viewDidLayoutSubviews, you can set the rect of the toolbar to sit just below the navigation bar and offset the top of the UIScrollView or UITableView.
Add button which you want in the storyboard.
Design your scrollview
self.view.sendSubviewToBack(scrollViewObj)(in the code)
This worked for me.

TableView scroll underlying top bar

Since I updated my app to iOS 7 new GUI I have a problem that I can't solve.
My app consists in a scrollable TableView. Trouble is that TableView scrolls underlying top bar, means that table doesn't consider top bar and extends till the top and it's ugly to see.
I tried removing check on "Extend edges under Top Bars" but it's the same.
How can I solve this?
One solution is: set the table view's contentInset and scrollIndicatorInsets to have a top inset of 20. The table view will still underlap the status bar, but it will be completely visible when scrolled all the way.
If you don't like that solution, and you want a permanent empty area behind the status bar, you will have to change the way you pin/position the top of the table view, to allow for the status bar. How you do this depends on whether you are using auto layout. If you are, just pin to the top layout guide. If you are not, you will have to use the "delta" field provided in the nib editor.
If you are using a UITableViewController, however, you are not in charge of the top of the table view; it is a full-screen view and it is the view controller's main view. This is quite a troublesome situation, actually. I have resorted to two solutions:
Put the whole thing into a UINavigationController in order to get the nav bar to "run interference" for me.
Or, embed the table view controller in a custom parent view controller just so that I can position the top of the table view.
In UINavigationController I created an UIView, which goes under status bar but in front of embed controller (the Table), so table disappear behind this view and status bar is always on top.
var patch: UIView!
override func viewDidLoad() {
super.viewDidLoad()
patch = UIView(frame: CGRectMake(0, 0, view.bounds.width, 20))
patch.backgroundColor = UIColor.redColor()
self.view.addSubview(patch)
}
Then I make it disappear when screen goes in Landscape (in iOS9, status bar automatically disappear in Landscape) and make it reappear when screen goes in Portrait.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
patch.hidden = true
} else {
patch.hidden = false
}
}

Resources