#14 Banner_ScorllView

--

寫個無限輪播圖,網上有100萬種作法🧐,但重點是能體現需求的那種做得出來就好,優化的事暫放在後面🤣,這次使用ScrollView 來寫,以下分享!

需求:

● 不能用storyboard

● 自動無限輪播

● 需從網路抓圖片,並存到手機中下次使用,且順序要對!

● 用 ”SnapKit” 寫layout (需 cocoapods 安裝)

● 要pageControll

● 版本10.3

● 需要進度條

開始前先上畫面:

設計思維架構:見下圖~

在一個scrollView中,塞入3個imageView。

當user滑動畫面時,使顯示畫面一直保持只顯示中間的imgView,

畫面的變化就是靠 img 一張張換上去!

接下來,因為大家都是大神,就不一行行看程式碼(可以去Github載),我把相關重點整理出來:

  1. 使scrollView 一直顯示中間:這是scrollView delegate func ( scrollViewDidEndDecelerating )

見下圖,user在哪個位置就可以判斷了,後續下相應的對策~

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {// 螢幕左邊滑到底,偏移到中間viewif scrollView.contentOffset.x == 0 {bannerScrollView.contentOffset = CGPoint(x: screenWidth, y: 0)imgIndex -= 1showBanner()}// 螢幕右邊滑到底,偏移到中間viewif scrollView.contentOffset.x == screenWidth * 2 {bannerScrollView.contentOffset = CGPoint(x: screenWidth, y: 0)imgIndex += 1showBanner()}startTimer()}

2. 版本10.3:這版本主要遇到問題是UISence 要iOS13 才能用,因此如果必需要用13以下版本開發,AppDelegate & SceneDelegate 改寫如下

AppDelegate:

import UIKit@UIApplicationMainclass AppDelegate : UIResponder, UIApplicationDelegate {var window : UIWindow?func application(_ application: UIApplication,didFinishLaunchingWithOptionslaunchOptions: [UIApplication.LaunchOptionsKey : Any]?)-> Bool {if #available(iOS 13, *) {// do only pure app launch stuff, not interface stuff} else {self.window = UIWindow()let vc = ViewController()self.window!.rootViewController = vcself.window!.makeKeyAndVisible()self.window!.backgroundColor = .white}return true}}

SceneDelegate :

import UIKit@available(iOS 13.0, *)class SceneDelegate: UIResponder, UIWindowSceneDelegate {var window : UIWindow?func scene(_ scene: UIScene,willConnectTo session: UISceneSession,options connectionOptions: UIScene.ConnectionOptions) {if let windowScene = scene as? UIWindowScene {self.window = UIWindow(windowScene: windowScene)let vc = ViewController()self.window!.rootViewController = vcself.window!.makeKeyAndVisible()self.window!.backgroundColor = .white}}}

3. SnapKit : 附上github,如果不會用cocoapods 安裝,可參考我之前文章~

4. 網路抓圖順序問題:

這很麻煩,因為網路抓圖他不能去控制每張圖誰先回來誰後回來(畢竟圖片有大有小),因此我想了許久,最後用Dictionary 再多包一層的方式解決,如果大神有更好的方法,請不令告知🥺
我的方法如下:

寫一個方法,把index & 網上抓回來的image 傳進來,再回傳一個排列好的[UIImage]

把index & image 存進 Dictionary,就會拿到Dictionary array

然後用Dictionary 中的 .sort(by:) 方法排列,注意要指定insex排序~

但轉出來的array 不是我們要的,因為他每項都多包了.Element

因此要再轉一次,那因為我們只要 [UIImage] 就好,所以這次轉型,順便把image 資料抽出來單獨存成array(一行程式做2件事),就可以回傳了😎

程式碼如下:

class ViewController: UIViewController {// 宣告一個實體變數Dictionaryprivate var willSortImgs = [Int:UIImage]()// 重新排列網路抓到的資料private func getOrderImgs(imageIndex: Int, image: UIImage) -> [UIImage] {willSortImgs[imageIndex] = image// 重新排列(小到大)
// 指定 key 去排序
// 轉出來的array 每個項目會帶.Element
let sorttings = willSortImgs.sorted { firstItem, secondItem inreturn firstItem.key < secondItem.key}
// 再轉一次,順便把 Dictionary 中的 value 單獨抽出來輸出成 arrayvar didSortImgs = [UIImage]()for image in sorttings {didSortImgs.append(image.value)}return didSortImgs}}

大神網址供參考:

5. 辛苦抓回來的圖,存進Sandbox中(Documents),下次就不需重新下載~

關於SandBox 下次再寫一篇介紹~這邊先把寫法貼上~

// sandBox - Document Pathlet documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)// 存擋路徑,自定的檔名前,記得加 “/”
let savePath = documentsPath[0] + "/banner\(imgIndex).jpg"

拿值就用:

let img = UIImage(contentsOfFile: savePath)

存值就用:image 要轉成 .jpeg 存擋

private func saveToSandBox(_ image: UIImage, _ savePath: String){do{try image.jpegData(compressionQuality: 1)?.write(to: URL(fileURLWithPath: savePath))} catch {print("Save to Sandbox Fail!")}}

6. 剩下其他功能像Timer() , pageControl , 進度條等,這邊就不贅述了~

--

--