I find a lot of resources on how to hide the navigation bar on scroll, but I would like to have the navigation bar hidden on start, and then appear when starting to scroll. Like this animation from Design+Code app: https://imgur.com/a/SqDRD
You can use UIScrollViewDelegate for that.
Here is example code for hide navigation bar and tool bar with scroll:
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var toolBar: UIToolbar!
#IBOutlet weak var webV: UIWebView!
var lastOffsetY :CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
webV.scrollView.delegate = self
let url = "http://apple.com"
let requestURL = NSURL(string:url)
let request = NSURLRequest(URL: requestURL!)
webV.loadRequest(request)
}
//Delegate Methods
func scrollViewWillBeginDragging(scrollView: UIScrollView){
lastOffsetY = scrollView.contentOffset.y
}
func scrollViewWillBeginDecelerating(scrollView: UIScrollView){
let hide = scrollView.contentOffset.y > self.lastOffsetY
self.navigationController?.setNavigationBarHidden(hide, animated: true)
toolBar.hidden = hide
}
}
Using the willBeginDragging and didEndDragging you can accomplish what you want. Here there is a simplified version of it, you may need to modify it a little bit to obtain the desired effect you want, but it is an starting point.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
navigationController?.setNavigationBarHidden(false, animated: true)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
navigationController?.setNavigationBarHidden(true, animated: true)
}
}
Related
When I try to change color or tool nothing happens and it still drawing with deafult black pen.
class ViewController: UIViewController {
#IBOutlet weak var canvasView: PKCanvasView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupCanvasView()
}
private func setupCanvasView() {
canvasView.drawingPolicy = .anyInput
let toolPicker = PKToolPicker()
toolPicker.setVisible(true, forFirstResponder: canvasView)
toolPicker.addObserver(canvasView)
canvasView.becomeFirstResponder()
}
}
Can't fix this problem. What should I do?
Mainscroll is scrollview which was added into another internal UIView and i have UISlider to change internal view scale.
But, MainScroll.setZoomScale(CGFloat(sender.value), animated: true) this line is not work.
==> Here is my code.
override func viewDidLoad() {
super.viewDidLoad()
MainScroll.maximumZoomScale = 1
MainScroll.minimumZoomScale = 10
MainScroll.delegate = self
MainScroll.zoomScale = 5
}
#IBAction func Scrolling(_ sender: UISlider) {
MainScroll.setZoomScale(CGFloat(sender.value), animated: true)
}
Here is a minimal example for getting a UIScrollView to zoom.
The key is that you have to implement func viewForZooming(in scrollView: UIScrollView) -> UIView? and return a view (that is inside your scrollView) that is to be scaled (think of it like your 'document' or 'canvas' view).
Also, notice that zoom is working for the default pinch gestures on the scrollView as well.
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
// Assume that outlets exist on a storyboard
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var canvas: UIView! // A view that is added in the scrollView
#IBOutlet var slider: UISlider!
let minScale: CGFloat = 1
let maxScale: CGFloat = 10
let initialScale: CGFloat = 1
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
scrollView.minimumZoomScale = minScale
scrollView.maximumZoomScale = maxScale
scrollView.zoomScale = initialScale
slider.minimumValue = Float(minScale)
slider.maximumValue = Float(maxScale)
slider.value = Float(initialScale)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return canvas
}
#IBAction func sliderValueChanged(_ sender: UISlider) {
scrollView.zoomScale = CGFloat(sender.value)
}
}
I have a UIView subclass (a graph) embedded inside of a UIScrollview inside of a UIViewController which is one of a UIPageViewControllers pages... I'm trying to disable scrolling of the UIPageViewController so that the user scan scroll to see the far left of the graph without paging back. How can I do this? Is a ScrollView even the correct tool for this job?
class HistoricalHealthDataViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var graphScrollView: UIScrollView!
#IBOutlet weak var hockeyTrackerGraphView: HockeyTrackerGraphView! {
didSet {
self.hockeyTrackerGraphView.graphableObjects = HFRGraphableObjects
}
}
var HFRGraphableObjects: [HockeyTrackerGraphableObject] = []
override func viewDidLoad() {
super.viewDidLoad()
graphScrollView.delegate = self
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let parentPageViewController = parent as? HistoricalPageViewController {
for gestureRecognizer in parentPageViewController.gestureRecognizers {
print("gestureRecognizer")
gestureRecognizer.isEnabled = false
}
}
}
}
You don't show the code for it, but I'm assuming you implement UIPageViewControllerDataSource protocol and somewhere you have the methods:
func pageViewController(UIPageViewController, viewControllerBefore: UIViewController) -> UIViewController? {
}
func pageViewController(UIPageViewController, viewControllerAfter: UIViewController) -> UIViewController? {
}
If you arrange for those methods to return nil while paging is disabled, the page view won't scroll to a different page.
I want to update the layout of a popover, I've already tried using the setNeedsLayout() and layoutIfNeeded() methods but couldn't find a solution.
I have a simple screen with 4 buttons that call a popover, within the popover there are 2 extra buttons:
Button -> hides button3, and moves button1 and button2 to the left.
reposition -> changes the sourceview of the popover from the button1/2/3/4 (sender) to button4.
Picture of the layout
The problem I have is that I'm not able to update/refresh the screen, and the popover keeps pointing to the former source view position.
Picture of the popover
Somehow I need a method that updates my view and which is stronger than layoutIfNeeded() because if the screen is changed from portrait/landscape the popover changes automatically. Here I changed portrait/landscape/portrait
GitHub of the project. GitHub
The code used is the following:
class ViewController: UIViewController, ButtonDidDisappearDelegate {
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
#IBOutlet weak var button4: UIButton!
#IBOutlet weak var constrain2: NSLayoutConstraint!
var popVC: PopVC?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonPopover(_ sender: UIButton) {
configureAndPresentPopover(from: sender)
}
func buttonDisappear() {
self.constrain2.constant = 100
self.button3.isHidden = !self.button3.isHidden
if !self.button3.isHidden {
self.constrain2.constant = 10
}
}
func repositionPopover() {
DispatchQueue.main.async {
if let popVC = self.popVC {
self.self.popoverPresentationController(popVC.popoverPresentationController!, willRepositionPopoverTo: self.button4.bounds, in: self.button4)
}
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
}
extension ViewController: UIPopoverPresentationControllerDelegate {
func configureAndPresentPopover(from sender: UIButton) {
popVC = storyboard?.instantiateViewController(withIdentifier: "popVC") as? PopVC
guard let popVC = popVC else {
return
}
popVC.popOverDelegate = self
popVC.modalPresentationStyle = .popover
let popOverVC = popVC.popoverPresentationController
popOverVC?.delegate = self
popOverVC?.sourceView = sender
popOverVC?.sourceRect = sender.bounds
popVC.preferredContentSize = CGSize(width: 300, height: 60)
popOverVC?.permittedArrowDirections = .down
self.present(popVC, animated: true)
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
func popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController,
willRepositionPopoverTo rect: CGRect,
in view: UIView) {
popoverPresentationController.sourceView = view
popoverPresentationController.sourceRect = rect
}
}
and the popover controller:
protocol ButtonDidDisappearDelegate: class {
func configureAndPresentPopover(from sender: UIButton)
func buttonDisappear()
func repositionPopover()
}
class PopVC: UIViewController {
weak var popOverDelegate: ButtonDidDisappearDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var popoverButton: UIButton!
#IBAction func popoverAction(_ sender: Any) {
popOverDelegate?.buttonDisappear()
DispatchQueue.main.async {
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
#IBAction func reposition(_ sender: Any) {
popOverDelegate?.repositionPopover()
}
}
You need to specify the new position of the arrow and ask for the layout of the presentation controller's view, not only the presented view.
Try :
func repositionPopover() {
let popOverController = presentedViewController?.presentationController as? UIPopoverPresentationController
popOverController?.sourceView = button4
popOverController?.containerView?.setNeedsLayout()
popOverController?.containerView?.layoutIfNeeded()
}
You can try setting the sourceview and sourcerect once again. call this function when the sourcview bounds are changed.
In a UIViewController, I instantiate a WKWebView and set the view controller as the WKWebiew scrollview's delegate:
class MyViewController: UIViewController, UIScrollViewDelegate {
var webView : WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView(frame: self.view.frame)
view = webView
webView.loadHTMLString("<h1>hello</h1>", baseURL: NSURL(string: "http://www.google.com")!)
webView.scrollView.delegate = self
}
func scrollViewDidScroll(scrollView: UIScrollView) {
println("didScroll")
}
The problem I have is that scrollViewDidScroll is called as soon as the WKWebView is created (ie without the user actually scrolling anything).
Why is that and how can I prevent it ?
Set a bool value to true in scrollViewWillBeginDragging method, which is called only when user scrolls and it is not automatically called by WKWebview
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
scrollByDragging = true
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !scrollByDragging{
UIView.animate(withDuration: 0.01) {
scrollView.setContentOffset(.zero, animated: true)
}
}
}
If you were to use contentOffset, you would be able to detect when the user scrolls.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y >= 0 {
print("didScroll")
}
}
If your UIScrollView is inside a UINavigationController, contentOffset.y will start at -44, but practically 0 is fine to use.