我已经创建了一个imageViewer,它将使用一个String数组进行初始化,并将根据需要下载并显示图像。
在回顾的同时,你能给我一个关于在didReceiveMemoryWarning里做什么的建议吗?
class PagedScrollViewController:UIViewController,UIScrollViewDelegate {
var imagesUrls:[String]?
var imagesDownloads:[ImageDownload?] = [ImageDownload]()
var downloadsResponse:[DownloadResponse?] = [DownloadResponse?]()
var pageImages:[UIImage?] = [UIImage]()
var pageViews:[ImageDownloadView?] = [ImageDownloadView]()
var scrollView:UIScrollView = UIScrollView()
var pageControl:UIPageControl = UIPageControl()
var viewingPage = -1
init(images:[String]) {
super.init(nibName: nil, bundle: nil)
self.imagesUrls = images
self.title = "Image viewer";
self.view.backgroundColor = UIColor.blackColor()
var pageCount = self.imagesUrls!.count
self.scrollView.pagingEnabled = true
self.scrollView.delegate = self
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.showsVerticalScrollIndicator = false
// Set up the page control
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = pageCount;
//Add
self.pageControl.setTranslatesAutoresizingMaskIntoConstraints(false)
self.scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(self.pageControl)
self.view.addSubview(self.scrollView)
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["control"] = self.pageControl;
viewsDict["scrollView"] = self.scrollView;
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[scrollView]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[control]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[scrollView(400)]-[control]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
// Set up the array to hold the views for each page
for (var i = 0; i < pageCount; ++i) {
self.pageViews.append(nil)
self.pageImages.append(nil)
self.imagesDownloads.append(nil)
self.downloadsResponse.append(nil)
}
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func loadVisiblePages() {
// First, determine which page is currently visible
var pageWidth:CGFloat = self.scrollView.frame.size.width;
var page = Int(floor((self.scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)));
/*
Check that page have changed, in case that user drag left in first page, or drag right in last page
a 'scrollViewDidEndDecelerating' is fired
*/
if self.viewingPage != page {
self.viewingPage = page
// Update the page control
self.pageControl.currentPage = page;
// Work out which pages we want to load
var firstPage = page - 1;
var lastPage = page + 1;
// Purge anything before the first page
for (var i=0; i<firstPage; i++) { self.purgePage(i) }
for (var i=firstPage; i<=lastPage; i++) { self.loadPage(i) }
for (var i = lastPage+1 ; i < self.pageImages.count ; i++) { self.purgePage(i) }
}
}
func loadPage(page:Int) {
if page < 0 || page >= self.pageImages.count {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Load an individual page, first seeing if we've already loaded it
var pageView:UIView? = self.pageViews[page];
if pageView == nil {
var frame:CGRect = self.scrollView.bounds;
frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.y = 0.0
var newPageView:ImageDownloadView = ImageDownloadView()
newPageView.frame = frame;
if(self.imagesDownloads[page] == nil && self.pageImages[page] == nil) {
self.downloadsResponse[page] = { (pageNumber:Int!, actionType:ImageDownload.ActionType, progress:Float, result:UIImage!) -> Void in
switch actionType {
case ImageDownload.ActionType.STARTING :
if self.pageViews[pageNumber] != nil {
//DO NOTHING FOR NOW
}
case ImageDownload.ActionType.DOWNLOADING :
if self.pageViews[pageNumber] != nil {
self.pageViews[pageNumber]?.setDownloadProgress(progress)
}
case ImageDownload.ActionType.COMPLETED :
self.pageImages[pageNumber] = result;
if self.pageViews[pageNumber] != nil {
self.pageViews[pageNumber]?.showImage(result)
}
}
}
self.imagesDownloads[page] = ImageDownload()
self.imagesDownloads[page]?.connect(self.imagesUrls![page], pageNumber:page, downloadReponse:self.downloadsResponse[page]!)
} else if self.pageImages[page] != nil {
newPageView.showImage(self.pageImages[page]!)
}
self.scrollView.addSubview(newPageView)
self.pageViews[page] = newPageView
}
}
func purgePage(page:Int) {
if page < 0 || page >= self.pageImages.count {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Remove a page from the scroll view and reset the container array
var pageView:UIView? = self.pageViews[page];
if pageView != nil {
pageView?.removeFromSuperview()
self.pageViews[page] = nil
}
}
override func viewDidAppear(animated:Bool) {
super.viewDidAppear(animated)
// Set up the content size of the scroll view
var pagesScrollViewSize:CGSize = self.scrollView.frame.size;
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(self.pageImages.count), pagesScrollViewSize.height);
// Load the initial set of pages that are on screen
self.loadVisiblePages()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
//Clean not visible images ?
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView!) {
self.loadVisiblePages()
}
}class ImageDownloadView: UIView {
var imageViewer:UIImageView = UIImageView()
var roundProgress:RoundProgress = RoundProgress(frame:CGRectMake(0, 0, 60, 60))
override init() {
super.init()
self.imageViewer.contentMode = UIViewContentMode.ScaleAspectFit;
self.imageViewer.setTranslatesAutoresizingMaskIntoConstraints(false)
self.roundProgress.setTranslatesAutoresizingMaskIntoConstraints(false)
self.addSubview(self.imageViewer)
self.addSubview(self.roundProgress)
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["image"] = self.imageViewer
viewsDict["progress"] = self.roundProgress
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[image]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[image]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[progress(60)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[progress(60)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.addConstraint(NSLayoutConstraint(item:self.roundProgress,
attribute:NSLayoutAttribute.CenterX,
relatedBy:NSLayoutRelation.Equal,
toItem:self,
attribute:NSLayoutAttribute.CenterX,
multiplier:1,
constant:0))
self.addConstraint(NSLayoutConstraint(item:self.roundProgress,
attribute:NSLayoutAttribute.CenterY,
relatedBy:NSLayoutRelation.Equal,
toItem:self,
attribute:NSLayoutAttribute.CenterY,
multiplier:1,
constant:0))
}
func setDownloadProgress(progress:Float) {
self.roundProgress.setProgress(progress / 100)
}
func showImage(image:UIImage) {
self.imageViewer.image = image
self.roundProgress.removeFromSuperview()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
}
}typealias DownloadResponse = (pageNumber:Int!, actionType:ImageDownload.ActionType, progress:Float, result:UIImage!) -> Void
class ImageDownload:NSObject, NSURLConnectionDataDelegate {
var data = NSMutableData()
var pageNumber:Int?
var downloadReponse:DownloadResponse?
var contentLength:Int64?
enum ActionType:CInt {
case STARTING = 1, DOWNLOADING, COMPLETED
}
func connect(query:String, pageNumber:Int, downloadReponse:DownloadResponse) {
self.pageNumber = pageNumber
self.downloadReponse = downloadReponse
var url = NSURL.URLWithString(query)
var request = NSURLRequest(URL: url)
var conn = NSURLConnection(request: request, delegate: self, startImmediately: false)
conn.start()
}
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
contentLength = response.expectedContentLength
downloadReponse!(pageNumber: self.pageNumber, actionType: ActionType.STARTING, progress:0.0, result: nil)
}
func connection(connection: NSURLConnection!, didReceiveData conData: NSData!) {
self.data.appendData(conData)
var progress:Float = Float(self.data.length * 100) / Float(self.contentLength!)
downloadReponse!(pageNumber: self.pageNumber, actionType: ActionType.DOWNLOADING, progress:progress, result: nil)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
downloadReponse!(pageNumber: self.pageNumber, actionType: ActionType.COMPLETED, progress:100.0, result: UIImage(data: self.data))
}
}class RoundProgress:UIView {
var progressCircle = CAShapeLayer();
var progress:Float = 0.1;
override init(frame: CGRect) {
super.init(frame: frame)
self.bounds = frame
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
let centerPoint = CGPoint (x: self.bounds.width / 2, y: self.bounds.width / 2);
let circleRadius : CGFloat = self.bounds.width / 2 * 0.83;
var circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true );
progressCircle.path = circlePath.CGPath;
progressCircle.strokeColor = UIColor.whiteColor().CGColor;
progressCircle.fillColor = UIColor.clearColor().CGColor;
progressCircle.lineWidth = 2.5;
progressCircle.strokeStart = 0;
progressCircle.strokeEnd = 0.0;
self.layer.addSublayer(progressCircle);
}
func setProgress(progress:Float) {
self.progress = progress
progressCircle.strokeEnd = CGFloat(self.progress)
self.setNeedsDisplay()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}发布于 2014-09-06 18:47:15
您的ImageDownload类有一些问题。
首先,我要指出我不喜欢这个名字。这应该更像ImageDownloader,但这是一个相当小的抱怨。
更大的问题是,我们采取一个完成块和完成块预期将响应3个不同的步骤,在下载图像的过程中。也许更大的问题是,我们根本不在乎下载可能会失败。我们还没有包括连接失败条件的委托方法。
我们也不公开允许对象的用户取消下载请求的任何方法(例如,当视图滚动到屏幕时)。因此,如果终端用户不在wi上,我们就有可能浪费他们的移动数据。
因此,我们需要添加一个方法来取消下载。我们还需要一种方法来处理下载失败或被取消的异步情况。
抑制将两个状态添加到您的enum中的冲动,并大幅增加完成块的复杂性。相反,我们需要实现一个协议-委托模式。
protocol ImageDownloaderDelegate {
required func imageDownloaderDidConnect()
required func imageDownloaderDidComplete(image: UIImage)
optional func imageDownloaderDidReceiveData(progress: Float)
optional func imageDownloaderDidFail(error: NSError)
optional func imageDownloaderWasCanceled()
}显然,如果您认为合适,可以向这些方法添加任何附加参数。
现在,我们向我们的ImageDownloader类添加一个值:
var delegate: ImageDownloaderDelegate?我们在适当的时候在委托上调用这些方法。我们应该确保在调用这些方法时使用可选链接,特别是在可选方法上:
self.delegate?.imageDownloaderDidReceiveData?(0.3)当然,这意味着无论是谁使用ImageDownloader,现在都需要实现这些方法,并准备好在调用它们时响应它们。
这使我们避免了巨大的、丑陋的开关,它被关闭而变得更加丑陋,并且只有在添加我指出的其他两种场景的情况时才会变得更丑。相反,我们只实现了这些2-5个方法。总的来说,它的代码行数并不少。差不多是一样的。但是每一种方法都是独立的。它们更小更分散。这是5个非常不同的事件,它们需要作为这样的事件来处理。
NSURLConnection使用协议委托模式有一个很好的理由,我们不应该试图将其压缩到一个完成块中。
https://codereview.stackexchange.com/questions/62144
复制相似问题