In Swift scroll view delegate is not working - swift

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.

Related

ScrollView Constraint problem threw storyboard

So I maybe successfully created a scroll view following a tutorial but something is terribly wrong. So I'm trying to create a scroll view with a table view inside and some buttons and labels and everything works fine but as soon as I add a single constraint it all goes just white with no explanation. I would assume content view is messing things up but I'm not sure, thx in advance!
So following some other people problems I tried filling constraints programmatically and doing views and subviews programmatically aswell, keep in mind tho that rest of constraints I did on storyboard. Btw I have tried equal width and height on newView --> scroll view and nothing seems to change.
my view hierarchy looks like this
myViewController -> View -> scrollView -> newView
class ThirdViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.addSubview(newView)
newView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
newView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
newView.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
newView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
//tableView1.dataSource = self
//tableView1.delegate = self
//tableView1.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
}
#IBOutlet weak var newView: UIView!
override func viewWillLayoutSubviews() {
scrollView.contentSize = CGSize(width: 375, height: 1950)
}
}
Sorry, if question is layout pretty badly - I'm not that pro!

Swift - Display header with animation

I'm kinda new to Swift coding and I wanted to train myself by finding some layouts and try to implement them using AutoLayout.
Here is a screen shot of a screen I try to implement
https://i.imgur.com/9yzvnzp.png
The behaviour is wanted here is that on scrolling the picture should fade away and the title should be set like so
https://i.imgur.com/bLhyTGs.png
Could anybody help me on how am I supposed to do that ?
Should I use a ScrollView ? UITableView ? UICollectionView ?
I'm actually working on an application that does something similar.
So, the first thing you want to do is create a UIView for the background, i.e. the blue part that shows Squirtle. This view can take up the entire view controller. Connect it to your UIViewController via IBOutlet and call it backgroundView.
Next, we want to lay a UIScrollView over backgroundView. This scroll view should be constrained to the top, bottom, leading, and trailing edges of the superview so that it covers the entire view controller's frame. Connect this UIScrollView to your UIViewController via IBOutlet and name it scrollView. Also, make sure that scrollView's backgroundColor is set to clear. This way, we'll be able to see backgroundView underneath our scrollView.
Within your UIViewController, you'll want to set scrollView's delegate to self inside of viewDidLoad. Your UIViewController's code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
We'll set delegate methods for scrollView later. These will help us know when the scroll view has begun scrolling to the top of our SquirtleViewController.
Now, our scrollView has a clear background, and doesn't actually display anything to our user. This is how we want it for now. We want the top of our scrollView to be clear so that we can see backgroundView behind it, and the bottom of our scrollView to have another view, i.e. the "content view" that shows STATS, EVOLUTIONS, MOVES, HP, etc.
So, let's add another UIView as a subview of our scrollView. Connect it to SquirtleViewController via IBOutlet and name it contentView.
Now, we need to create a constraint between contentView's top edge and scrollView's top edge. This constraint's constant needs to be equal to the height of backgroundView's content. This way, our contentView won't cover up what we want to see from backgroundView. We should also save this height as backgroundViewContentHeight so that we can reference it later.
We also need contentView's leading, trailing, and bottom constraints to be equal to those of its superview, i.e. scrollView. These do not need to be connected via IBOutlets.
Also, give contentView a height and width constraint and connect these as contentViewHeight and contentViewWidth respectively to SquirtleViewController via IBOutlet. This will help us set the contentSize of our scrollView later.
Your code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New lines of code
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
Now, when SquirtleViewController lays out its subviews, we are going to want to set the constant properties of contentViewHeight and contentViewWidth. This will set a contentSize for our scrollView. We want our contentViewWidth to simply be the size of our SquirtleViewController's view's width. Our contentViewHeight's constant will be a little different though, as it depends on the height of our contentView's subviews. For this example, we'll say the contentViewHeight's constant should be 1200. The code should now look like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
//New lines of code
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
From here, you can add subviews to backgroundView and contentView as you see fit in order to show the pokemon (backgroundView) as well as the pokemon's stats (contentView). But, you still need to know how to change the content of your backgroundView depending upon how much we have scrolled the scrollView. This is where the UIScrollViewDelegate helps us out.
There is a method in UIScrollViewDelegate that is called every time our scrollView's contentOffset.y changes. This basically means every time we change the amount that our scrollView has scrolled, this method will be called.
Inside of this method, we can cross reference the amount we've scrolled with the height of the background view. As our scrollView's contentOffset.y approaches the height of our backgroundView's content, we can fade out the image of Squirtle and fade in a UILabel that simply says "Squirtle" (like in your example).
So, in your case, I would suggest adding a UIImage of Squirtle as a subview of contentView and connect it via IBOutlet as pokemonImageView. pokemonImageView should have its frame partially outside of the contentView (like in your example). If you do this, ensure that contentView's clipsToBounds property is set to false.
I would also add a UILabel as a subview of backgroundView and connect it to SquirtleViewController via IBOutlet as pokemonNameLabel. When our SquirtleViewController's view loads, we should set pokemonNameLabel.alpha equal to zero so that it is initially hidden.
Your code should now look like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
//New line of code
#IBOutlet var pokemonNameLabel: UILabel!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New line of code
#IBOutlet var pokemonImageView: UIImageView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
//New line of code
pokemonNameLabel.alpha = 0
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
Now we simply need to add the scrollViewDidScroll method inside of our class extension. This is the method that will tell us our scrollView's contentOffset.y property so that we know how much we've scrolled. As this number increases, our pokemonImageView's alpha should decrease to zero and our pokemonNameLabel's alpha should increase towards one.
Inside of this method, I would divide our scrollView.contentOffset.y by the height of our backgroundView minus the height of the pokemonNameLabel. We can then use this decimal to set our respective alphas of pokemonImageView and pokemonNameLabel.
Your code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
//New line of code
#IBOutlet var pokemonNameLabel: UILabel!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New line of code
#IBOutlet var pokemonImageView: UIImageView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
//New line of code
pokemonNameLabel.alpha = 0
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//Subtracting contentView.frame.height from scrollView.frame.height
let alphaDecimal = scrollView.contentOffset.y / (backgroundViewContentHeight - pokemonNameLabel.frame.height)
pokemonNameLabel.alpha = alphaDecimal
pokemonImageView.alpha = 1 - alphaDecimal
}
}
Now, as we scroll up and what can be seen of the backgroundView shrinks, our pokemonImageView will fade out and our pokemonNameLabel will fade in. There will be a point in-between where both will have an alpha of 0.5. You can mess with this scrollViewDidScroll method as you see fit to make this work best for you.

Swift - scrollViewDidScroll not working with WKWebView

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 {

UIView's animate method not appearing

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.

Scroll Position of UITextView at the top

I am using swift and want a UITextView to be at the top when the view launches. At the moment when I launch the app the UITextView is scrolled to the end. I have tried looking online and think scrollRangeToVisible might work but do not know how to use it in swift.
import UIKit
class ThirdViewController: UIViewController {
#IBOutlet weak var FunFact: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
FunFact.scrollRangeToVisible(0, 0)
// Do any additional setup after loading the view.
}
}
Add this to your ViewController:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
textView.layoutIfNeeded()
textView.contentOffset = CGPoint.zero
}
This scrolls the UITextView to the top. Since it only knows its size, when it is layouted, "layoutIfNeeded" is needed after you changed the text (i changed it in viewDidLoad).
Try this:
var zeroOffset = CGPoint.zeroPoint
FunFact.contentOffset(zeroOffset)
This should bring the offset to 0 (the offset is an indication of how far the current position is from the initial one)