I have the following code:
extension SegmentedControlViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// this checks for whether contentOffset.x is around 10 (contented shifted to the right by 10 points)
if abs(scrollView.contentOffset.x - 10) < 1 {
print("works")
}
}
}
#IBOutlet weak var scrollView: UIScrollView!
then I call the function in the viewDidLoad like this:
scrollViewDidScroll(scrollView)
Although I am always getting the error message
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
What am I doing wrong, how to Get the scroll view to not be nil.
Assign the scroll view's .delegate in viewDidLoad() and start scrolling:
class SegmentedControlViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// assign the delegate
scrollView.delegate = self
}
}
extension SegmentedControlViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// this checks for whether contentOffset.x is around 10 (contented shifted to the right by 10 points)
if abs(scrollView.contentOffset.x - 10) < 1 {
print("works")
}
}
}
Edit
If you want to check if the scroll view content has been dragged to the right more than 10 points, use this:
extension SegmentedControlViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// this checks for whether contentOffset.x is around 10 (contented shifted to the right by 10 points)
if scrollView.contentOffset.x < 10 {
print("scroll content has been dragged to the right more than 10 points")
}
}
}
Related
I'm trying to add a shadow to my (custom) nav bar when a user scrolls using scrollViewDidScroll but it doesn't do anything. I have the same exact code on another view controller but it has a tableView instead of a WKWebView and it works fine.
I tried adding webView.scrollView.delegate = self but I just get an error.
My code:
class ViewController: UIViewController {
#IBOutlet weak var webView: WKWebView!
#IBOutlet weak var navBar: UIView!
override func viewDidLoad() {
super.viewDidLoad()
addShadow()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let navigationBar = navBar
let offset = scrollView.contentOffset.y / 10
if offset > 1.5 {
navigationBar?.layer.shadowOpacity = 0.15
} else {
navigationBar?.layer.shadowOpacity = Float(((3 * offset) / 20)/1.5)
}
}
func addShadow() {
navBar.layer.shadowColor = UIColor.black.cgColor
navBar.layer.shadowOffset = CGSize(width: 0, height: 2.0)
navBar.layer.shadowRadius = 6.0
navBar.layer.masksToBounds = false
}
}
I tried adding webView.scrollView.delegate = self but I just get an error.
You didn't share what the error was. I suspect though that you didn't declare your view controller as UIScrollViewDelegate conforming, which caused the compile time error when trying to set self as the scroll view delegate.
Change your view controller declaration to this:
class ViewController: UIViewController, UIScrollViewDelegate {
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate{
#IBOutlet weak var scroll: UIScrollView!
#IBOutlet weak var Wbs: webview!
override func viewDidLoad() {
super.viewDidLoad()
scroll.backgroundColor=#colorLiteral(red: 0.4392156899, green: 0.01176470611, blue: 0.1921568662, alpha: 1)
scroll.delegate=self
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
print("as")
}
}
I always wonder I this code is not working, I have implemented scrollViewwillBegin dragging so when I scroll I get the message I didn't get any message , So what wrong here
Scrollviews without sufficient content (content size equal or smaller then the scrollview) wont get that delegate called because it has no scroll.
Make sure you have enough content and that all views you add to your scrollview have all 4 of their sides pinned, otherwise it wont work.
I am trying to make an app that utilizes some search feature and I am trying to make it so that after the search button is pressed, a view (which contains the search results) moves up from the bottom of the superview and replaces the search view. On the storyboard, the resultsView (of type UIView) is constrained so that its top is equal to the superview's bottom. After the search button is pressed, I would like to animate the view to move up and replace the view already at the bottom of the superview. The problem is, in the viewcontroller's class, when I call the resultsView, the animateWithDuration(NSTimeInterval) that is supposed to be associated with the UIView class is not appearing for me. May this be because the view is already constrained in place? Here is the code, simplified for this post:
import UIKit
import MapKit
class MapViewController: UIViewController, CLLocationManagerDelegate,
MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var distanceLabel: UILabel!
#IBOutlet weak var searchButton: UIButton!
#IBOutlet weak var searchView: UIView!
#IBOutlet weak var resultView: UIView!
#IBOutlet weak var resultNameLabel: UILabel!
#IBOutlet weak var resultDistanceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
self.resultView.isHidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sliderAdjusted(_ sender: Any) {
let int = Int(slider.value)
switch int {
case 1:
distanceLabel.text = "1 mile"
default:
distanceLabel.text = "\(int) miles"
}
}
#IBAction func searchButtonPressed(_ sender: Any) {
/*This is where, after calling a search function which is omitted
from this example, I would like to make the resultsView not
hidden and then animate it to sort of eclipse the search view*/
self.resultView.isHidden = false
self.resultView.animate(withDuration: NSTimeInterval)
/*The above line of code is not actually appearing for me.
After typing "self.resultView." the animate function is not being
given to me as an option for my UIView*/
}
}
I will also attach some images of the view controller so you can sort of get the idea. The results view is not visible in this image because its top is constrained to the superview's bottom, thus it is just out of the visible representation of its superview.
The first image is the view controller with the searchView highlighted. This is the view that I would like to be eclipsed by my resultView after the searchButton is pressed.
The second image is the same view controller with the resultView highlighted. As you can see, its top is constrained to be equal to the superview's bottom. This is the view that I would like to animate upwards into the superview and eclipse the searchView after the search button is pressed.
The methods for all the animate family are all class methods. Which means you call them on the class object not an instance.
You are trying to call
class func animate(withDuration: TimeInterval, animations: () -> Void)
so your code needs to look like
UIView.animate(withDuration: 0.5) {
//the things you want to animate
//will animate with 0.5 seconds duration
}
In the particular case it looks like you are trying to animate the height of resultView so you need an IBOutlet to that constraint. You could call it resultViewHeight.
UIView.animate(withDuration: 0.5) {
self.resultViewHeight.constant = theDesiredHeight
self.layoutIfNeeded()
}
Calling layoutIfNeeded() within the closure is the secret sauce to animating auto layout. Without that the animation will just jump to the and point.
I got a scrollView with some sub views added to it. Scrolling and zooming works fine.
What I would like to is when the scrollView is zoomed in, alter the behavior of the 1 finger touch to pan instead of scroll.
Right now if you are zoomed in and move around with 1 finger you can only scroll the zoomed in content. With two fingers you can pan around. This is the behaviour I would like to change.
I haven't found any solution to this issue and I don't really have any ideas except trying to alter the minimum and maximum touches required but it doesn't seem to work.
func scrollViewDidZoom(_ scrollView: UIScrollView) {
scrollView.panGestureRecognizer.maximumNumberOfTouches = 1
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
if scale == 1 {
scrollView.panGestureRecognizer.maximumNumberOfTouches = 2
}
}
I realized the issue I had was that each subview had their own scrollView (in my case my subviews wkWebViews). They were handling their own zoom even though I was trying to zoom from the UIScrollView.
I took these steps to solve this issue.
MyClass: UIViewController, UIScrollViewDelegate {
// This view should handle the zooming
#IBOutlet var scrollView: UIScrollView!
// .. and this view will contain all subviews to be zoomed
#IBOutlet var wrapper: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.delegate = self
self.addSViewsToScrollView()
}
func addViewsToScrollView () {
// MARK: - Step 1
// .. set wkWebView scrollview maximum and minimum zoom scale to 1.
let myArray: [WkWebView]? = [WkWebView(),WkWebView(),WkWebView()]
for view in myArray {
wkWebView.scrollView.maximumZoomScale = 1
wkWebView.scrollView.minimumZoomScale = 1
wkWebView.isUserInteractionEnabled = false // This is key otherwise it wont work
wkWebView.scrollView.isUserInteractionEnabled = false // .. this aswell
}
// MARK: - Step 2
// .. add all views to a wrapper view
for view in myArray {
self.wrapperView.addSubview(view)
}
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self.wrapper
}
}
By returning a single view the panning in the zoomed in state works fine. Also the wkWebView scrollViews do not interfere with the zooming anymore.
I have a TextView and a hidden button within a UIView, and I'm trying to detect when the user scrolls down to the bottom of a long list of text and to show the hidden button when they reach the bottom. I saw some old posts on how it was done in Obj-C using scrollViewDidScroll, but not really sure how to do that with swift, or how to do it with a TextView instead of a ScrollView. Any help would be great as I haven't gone very far with this one.
So far this is my attempt at translating the obj-c post to swift, but it hasn't worked for me, in fact I'm not even sure when the function is called:
import UIKit
class MainVC: UIViewController, UIScrollViewDelegate {
#IBOutlet var textView: UIScrollView!
#IBOutlet var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
func scrollViewDidScroll(textV: UIScrollView) {
if (textV.contentOffset.y >= textV.contentSize.height - textV.frame.size.height)
{
button.isHidden = false
}
}
}
Thanks for any help in advance :)
UITextView is subclass of UIScrollView and if you look to declaration, you will see, that it is UIScrollViewDelegate by default, so you can remove the UIScrollViewDelegate at the declaration of your controller. Instead, make your controller UITextViewDelegate which allows it to call scrollViewDidScrollMethod.
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView! {
didSet {
textView.delegate = self
}
}
#IBOutlet weak var button: UIButton! {
didSet {
button.hidden = true
}
}
func scrollViewDidScroll(scrollView: UIScrollView) {
button.hidden = scrollView.contentOffset.y + scrollView.bounds.height < scrollView.contentSize.height
}
}
(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
float bottomEdge = scrollView.contentOffset.y + scrollView.frame.size.height
if (bottomEdge >= scrollView.contentSize.height) {
self.yourButtonName.hidden = true
}
}
If the button is in the last of view(self.view) then I think you have to check that your contentOffset point is at the bottom of contentSize. So you could probably do something like:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
float bottomEdge = scrollView.contentOffset.y +scrollView.frame.size.height;
if (bottomEdge >= scrollView.contentSize.height) {
self.yourButtonName.hidden = true
}
}