问题
In Objective c i wrote download/upload with progressview in uitableviewcell (multiple upload/download) by AFNETWORKING. and work find it's can be update progressview/file/cell.
and Now im noob first time change to SWIFT programming and use urlsession.
code
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, URLSessionDelegate, URLSessionDataDelegate, URLSessionTaskDelegate {
//var dataArr:Dictionary<String,String> = [:]
var dataArr : NSMutableArray = NSMutableArray.init()
var myTableview:UITableView = UITableView.init()
let color = UIColor(red: 69/255, green: 57/255, blue: 169/255, alpha: 1.0)
let cellID: String = "customCell"
var progressBar : UIProgressView = UIProgressView.init()
let progressView : UIView = UIView.init(frame: CGRect(x: 100, y: 10, width: 100, height: 20))
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
{
let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
print(uploadProgress)
DispatchQueue.main.async {
self.progressBar.progress = uploadProgress
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
let textName: UILabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
textName.textColor=UIColor.black
textName.backgroundColor=UIColor.green
textName.text=dataArr[indexPath.row] as? String
print("\(dataArr[indexPath.row])");
textName.font=UIFont.systemFont(ofSize: 14)
cell.addSubview(textName)
progressView.backgroundColor = UIColor.red
progressView.tag=indexPath.row
let customKeys=["type","Facebook","Google","Twitter"];
let customsValues=["uploadFile","Mark","Lary","Goo"];
let customDatas=Dictionary(uniqueKeysWithValues: zip(customKeys,customsValues))
progressBar = UIProgressView.init(frame: CGRect(x: 0, y: 5, width: 100, height: 20))
progressBar.tag=indexPath.row
progressView.addSubview(progressBar)
cell.addSubview(progressView)
uploadImage(data_dict: customDatas, uploadImg: dataArr[indexPath.row] as! String)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let hCell:CGFloat = 50.0
return hCell
}
// override func viewWillAppear(_ animated: Bool) {
// super.viewWillAppear(animated)
// setNeedsStatusBarAppearanceUpdate()
// }
override var preferredStatusBarStyle: UIStatusBarStyle {
// Change font of status bar is white.
.lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
dataArr=["1.jpg","2.jpg","3.jpg"]
//print(dataArr)
let myScreen = UIScreen.main.bounds
let statusHieght = UIApplication.shared.statusBarFrame.height
if #available(iOS 13.0, *) {
let app = UIApplication.shared
let statusBarHeight: CGFloat = app.statusBarFrame.size.height
let statusbarView = UIView()
statusbarView.backgroundColor = color
statusbarView.tintColor = .white
view.addSubview(statusbarView)
statusbarView.translatesAutoresizingMaskIntoConstraints = false
statusbarView.heightAnchor
.constraint(equalToConstant: statusBarHeight).isActive = true
statusbarView.widthAnchor
.constraint(equalTo: view.widthAnchor, multiplier: 1.0).isActive = true
statusbarView.topAnchor
.constraint(equalTo: view.topAnchor).isActive = true
statusbarView.centerXAnchor
.constraint(equalTo: view.centerXAnchor).isActive = true
} else {
let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
statusBar?.backgroundColor = color
}
UINavigationBar.appearance().barTintColor = color
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
UINavigationBar.appearance().isTranslucent = false
let navBar = UINavigationBar(frame: CGRect(x: 0, y: statusHieght, width: myScreen.size.width, height: 44))
//navBar.isTranslucent=true
//navBar.backgroundColor = .red
let navItem = UINavigationItem(title: "SomeTitle")
let doneItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target:nil , action:#selector(ClickDone))
navItem.rightBarButtonItem = doneItem
navBar.setItems([navItem], animated: false)
self.view.addSubview(navBar)
let AllTopDistance=statusHieght+navBar.frame.size.height
let myView:UIView = UIView.init(frame: CGRect(x: 0, y: AllTopDistance, width: myScreen.size.width, height: myScreen.size.height-AllTopDistance))
myView.backgroundColor = .lightGray
myTableview=UITableView.init(frame: CGRect(x: 0, y: 0, width: myScreen.size.width, height: myScreen.size.height-AllTopDistance))
myTableview.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
print("\(statusHieght) \(myScreen.size.width) \(AllTopDistance)")
myTableview.delegate=self
myTableview.dataSource=self
myTableview.backgroundColor=UIColor.red
myView.addSubview(myTableview)
self.view.addSubview(myView)
}
@objc func ClickDone(){
print("Done")
}
func calculateTopDistance() -> CGFloat{
/// Create view for misure
let misureView : UIView = UIView()
misureView.backgroundColor = .clear
view.addSubview(misureView)
/// Add needed constraint
misureView.translatesAutoresizingMaskIntoConstraints = false
misureView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
misureView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
misureView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
if let nav = navigationController {
misureView.topAnchor.constraint(equalTo: nav.navigationBar.bottomAnchor).isActive = true
}else{
misureView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
}
/// Force layout
view.layoutIfNeeded()
/// Calculate distance
let distance = view.frame.size.height - misureView.frame.size.height
/// Remove from superview
misureView.removeFromSuperview()
return distance
}
@objc func uploadImage(data_dict : Dictionary<String,String>, uploadImg : String)
{
print("click \(data_dict)")
let image = UIImage(named: uploadImg)
// generate boundary string using a unique per-app string
let boundary = UUID().uuidString
let config = URLSessionConfiguration.default
//let session = URLSession(configuration: config)
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
// Set the URLRequest to POST and to the specified URL
var urlRequest = URLRequest(url: URL(string: "http://x.x.x.x/xxx/Labs.php")!)
urlRequest.httpMethod = "POST"
// Set Content-Type Header to multipart/form-data, this is equivalent to submitting form data with file upload in a web browser
// And the boundary is also set here
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
for (key, value) in data_dict {
print(key, value)
let fieldName = key
let fieldValue = value
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(fieldName)\"\r\n\r\n".data(using: .utf8)!)
data.append("\(fieldValue)".data(using: .utf8)!)
}
// Add the image data to the raw http request data
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"fileToUpload\"; filename=\"\(uploadImg)\"\r\n".data(using: .utf8)!)
data.append("Content-Type: image/png\r\n\r\n".data(using: .utf8)!)
data.append((image?.jpegData(compressionQuality: 1.0))!)
// End the raw http request data, note that there is 2 extra dash ("-") at the end, this is to indicate the end of the data
// According to the HTTP 1.1 specification https://tools.ietf.org/html/rfc7230
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
// Send a POST request to the URL, with the data we created earlier
session.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in
if(error != nil){
print("\(error!.localizedDescription)")
}
guard let responseData = responseData else {
print("no response data")
return
}
if let responseString = String(data: responseData, encoding: .utf8) {
print("Response data : \(responseString)")
}
}).resume()
}
}
?? How define URLSESSION update progressview in uitableview by cell/file Thank you.
回答1:
I'm curious how you did it in Objective-C with AFNetworking. At least conceptional, there shouldn't be a big difference to implementation in Swift with URLSession.
Imho your main problem about updating your progress is, that you share the progressView variable with a single UIView instance for all of your cells.
- you do not initialize a new progressView for each cell, but share one view for all cells
- because of 1,
cell.addSubview(progressView)
doesn't only add your progressView to that cell, it removes your progressView from the other cells as well, because a view can only have one parent view. - your progressView will have multiple UIProgressBars as subview. One for each time
tableView(_:cellForRowAt indexPath:)
is called - with
self.progressBar.progress = uploadProgress
you will always update the progressBar which was last initialized, because you don't have a reference to the other ones.
To get this to work in a clean way, I'd recommend you do some research to MVVM architecture.
- create a UITableViewCell subclass for your cell
- create a ViewModel class for that cell
- store a viewModel instance for each of your cells in your viewController (or better in a separate TableViewDataSource object)
- implement URLSessionDelegate protocol in your ViewModel and set the appropriate viewModel instance as delegate for your uploadTasks
For a quick and dirty fix:
Remove these lines:
var progressBar : UIProgressView = UIProgressView.init()
let progressView : UIView = UIView.init(frame: CGRect(x: 100, y: 10, width: 100, height: 20))
Add variable:
var uploadTasks: [URLSessionDataTask: IndexPath] = [:]
Add a helper function to calculate viewTag:
func viewTag(for indexPath: IndexPath) -> Int {
return indexPath.row + 1000
}
Change your tableView(_:cellForRowAt indexPath:)
to:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
let textName: UILabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
textName.textColor=UIColor.black
textName.backgroundColor=UIColor.green
textName.text=dataArr[indexPath.row] as? String
print("\(dataArr[indexPath.row])");
textName.font=UIFont.systemFont(ofSize: 14)
cell.addSubview(textName)
let progressView = UIView(frame: CGRect(x: 100, y: 10, width: 100, height: 20))
progressView.backgroundColor = UIColor.red
let customKeys=["type","Facebook","Google","Twitter"];
let customsValues=["uploadFile","Mark","Lary","Goo"];
let customDatas=Dictionary(uniqueKeysWithValues: zip(customKeys,customsValues))
let progressBar = UIProgressView.init(frame: CGRect(x: 0, y: 5, width: 100, height: 20))
progressBar.tag = viewTag(for: indexPath)
progressView.addSubview(progressBar)
cell.addSubview(progressView)
uploadImage(data_dict: customDatas, indexPath: indexPath)
return cell
}
Change your uploadImage method to:
@objc func uploadImage(data_dict : Dictionary<String,String>, indexPath : IndexPath) {
print("click \(data_dict)")
let uploadImg = dataArr[indexPath.row] as! String
let image = UIImage(named: uploadImg)
...
let task = session.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in
...
})
uploadTasks[task] = indexPath
task.resume()
}
Change your urlSession delegate method to:
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
print(uploadProgress)
guard let indexPath = uploadTasks[task] else { return }
let viewTag = viewTag(for: indexPath)
guard let progressBar = self.view.viewWithTag(viewTag) as? UIProgressView else { return }
DispatchQueue.main.async {
progressBar.progress = uploadProgress
}
}
来源:https://stackoverflow.com/questions/60200140/how-to-update-progressview-in-uitableview-cell-by-urlsession-download-upload-fi