-1

I'm working on an application and want my scrollview to lock in place while data is loading. Currently, when the user refreshes the scrollview immediately snaps back into place. How can I adjust my code so that the scrollview locks into place until the code inside it is fully executed? Here's my code so far:

@IBOutlet weak var scrollView: UIScrollView!
var refreshControl: UIRefreshControl!

@objc func refreshData(sender: UIRefreshControl){
    getItems()
  
    sender.endRefreshing()
}

here is my getItems() function:

   func getItems() {
    //Hit the web service Url
    let serviceUrl = "omitted"
    let url = URL(string: serviceUrl)
    //Download the json data
    if let url = url{
        //Create a URL Session
        let session = URLSession(configuration: .default)
        let request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 15.0)
        let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
            if error == nil {
                //Succeeded
                //Call the parse json function on the data
                self.parseJson(data!)
            }
            else {
                print("error occured in getItems")
            }
        })
        // Start the task
        task.resume()
    }
}
//Parse it out into DataModel structs

func parseJson(_ data:Data){
    //parse the data into DataModel structs
    do{
        //parse the data into a json object
        let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as! [Any]
        //loop through each result in the json array
        for jsonResult in jsonArray {
            //Cast json result as a dictionary
            let jsonDict = jsonResult as! [String:String]
            //create new bar data and set its properties
            let bardata = DataModel(name: jsonDict["Bar Name"]!, cover: jsonDict["Bar Cover"]!, deals: jsonDict["Drink Deals"]!)
            //add it to the array
            bar_info.append(bardata)
        }
    }
    catch{
        print("There was an error")
    }
    //call function here
    update_cover()
    
}

func update_cover(){
    var a_cover = "$5"
    var b_cover = "$5"
    var c_cover = "$5"
    var d_cover = "$5"
    
    for item in bar_info{
        if item.name == "omitted"{
            a = item.cover
        }
        
        else if item.name == "omitted"{
            b_cover = item.cover
        }
            
        else if item.name == "omitted"{
            c_cover = item.cover
        }
    
        else if item.name == "omitted"{
            d_cover = item.cover
        }
    }
    
    
    DispatchQueue.main.async {
        self.aCoverView.text = a_cover
        self.bCoverView.text = b_cover
        self.cCoverView.text = c_cover
       self.dCoverView.text = d_cover
    }
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Skii_club
  • 45
  • 4
  • Do you need to block user interaction, or make scroll view to return to the position where it was before reloading data? – DionizB Nov 01 '18 at 16:43
  • I need the scroll view to stay in place while the refresh control performs the functions within it. Sort of like Instagram or facebook works if you’re familiar with that. – Skii_club Nov 01 '18 at 17:32
  • Does `getItems()` do some async operation, like going over a network to refresh the data? In that case it will return immediately, before the request finishes. If this is the case, the refresh control should end refreshing in the completion handler of the network request. – Samantha Nov 01 '18 at 17:44
  • Can you add your `getItems()` method code? – DionizB Nov 02 '18 at 08:29
  • @DionizB I've updated my code so that it shows the getItems() method – Skii_club Nov 02 '18 at 18:10
  • @Samantha getItems() is doing an async operation, where should I place the end refresh function? I posted the getItems() code above. – Skii_club Nov 02 '18 at 18:12

4 Answers4

0

To lock scrollview use:

scrollView.isUserInteractionEnabled = false

and then unlock:

scrollView.isUserInteractionEnabled = true
Nimantha
  • 6,405
  • 6
  • 28
  • 69
canister_exister
  • 567
  • 4
  • 15
0

Just before relaoding the scrollview, store the content offset of the current position, then set it while reloading is done.

var currentPos:CGPoint?

func reloadScrollview() { 
    currentPos = scrollview.contentOffset

    // do your reloading stuff
    // layout your subviews inside the scrollview
    // set the content size based upon your subviews

    scrollview.contentOffset = currentPos

}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Ratul Sharker
  • 7,484
  • 4
  • 35
  • 44
0

It sounds like you are implementing pull-to-refresh so I assume you will want to make use of the scrollViewDidScroll delegate method:

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    let offset = scrollView.contentOffset // get the current offset
    scrollView.isUserInteractionEnabled = false // disable interaction
    scrollView.setContentOffset(offset, animated: false) // freeze the offset

}

Now you just need to configure the method.

trndjc
  • 11,654
  • 3
  • 38
  • 51
0

You can do something like this in getItems:

    func getItems() {
        //Hit the web service Url
        let serviceUrl = "omitted"
        let url = URL(string: serviceUrl)
        //Download the json data
        if let url = url{
            //Create a URL Session
            let session = URLSession(configuration: .default)
            let request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 15.0)
            let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
                DispatchQueue.main.async {
                    self.refreshControl.endRefreshing()
                }
                if error == nil {
                    //Succeeded
                    //Call the parse json function on the data
                    self.parseJson(data!)
                }
                else {
                    print("error occured in getItems")
                }
            })
            // Start the task
            task.resume()
        }
    }

The refresh control needs to end refreshing only after the network request finishes (i.e., in the completion handler). Because the request is asynchronous, getItems returns immediately while the request is still ongoing.

Samantha
  • 2,269
  • 2
  • 13
  • 25