LoginSignup
15
15

More than 5 years have passed since last update.

iOSのWidgetをAPIからデータを取得して表示させてみる

Last updated at Posted at 2015-02-19

Screen Shot 2015-03-02 at 1.20.49 PM.png
な感じで表示されるものを作ってみる。

準備に関してはこちら

開発する前にざっとドキュメントを読んでみる。

冒頭のWidgetの振る舞いについて意訳をしてみる。

Ensure that content always looks up to date
コンテツはいつも最新にしておけよ!

Respond appropriately to user interactions
いい感じで使いやすくしろよ!

Perform well (in particular, iOS widgets must use memory wisely or the system may terminate them)
ちゃんと動かせよ!

Avoid putting a scroll view inside a Today widget.
scroll viewは使わないで!

これってUIScrollViewのことじゃないよね???
なのでpageControlとscrollViewを利用して実装してみる。

Widget出来上がりサンプル

以下のような仮実装をしてみた。
>ボタンを押して画面遷移をさせるようにしてみた。
Screen Shot 2015-02-19 at 8.07.28 PM.png
Screen Shot 2015-02-19 at 8.08.39 PM.png

サンプルソース

TodayView.swift

TodayView.swift
import UIKit
import Foundation

public class TodayView : UIView {
    var currentPage: Int?

    required public init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override init(frame : CGRect) {
        super.init(frame : frame)
    }

    convenience init(_ currentPage: Int) {
        self.init(frame: CGRectZero)
        self.setup(currentPage)
    }

    func setup(currentPage: Int) {
        self.currentPage = currentPage
    }
}

TodayViewControllerサンプル

TodayViewControllerSample.swift
import UIKit
import NotificationCenter


class TodayViewController: UIViewController, NCWidgetProviding {
    @IBOutlet weak var pageControl: UIPageControl!
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var nextButton: UIButton!
    @IBOutlet weak var backButton: UIButton!

    let numberOfPages: Int = 5 // ページ数
    let viewHeight: CGFloat = 140 // 画面の高さ

    override func viewDidLoad() {
        super.viewDidLoad()

        // pageControlの準備
        pageControl.numberOfPages = numberOfPages

        // scrollViewの準備
        scrollView.contentSize = CGSize(
            width: CGFloat(numberOfPages) * scrollView.frame.size.width,
            height: viewHeight
        )

        // Widgetに高さを明示的に教える
        self.preferredContentSize = CGSizeMake(0, viewHeight);

        self.updateScrollViewContent()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
        // Perform any setup necessary in order to update the view.

        // If an error is encountered, use NCUpdateResult.Failed
        // If there's no update required, use NCUpdateResult.NoData
        // If there's an update, use NCUpdateResult.NewData

        completionHandler(NCUpdateResult.NewData)
    }

    // 戻るボタンアクション
    @IBAction func backButton(sender: UIButton) {
        if pageControl.currentPage > 0 {
            pageControl.currentPage--
            nextButton.hidden = false
        }
        if pageControl.currentPage == 0 {
            backButton.hidden = true
        }
        self.updateScrollViewContentOffset()
    }

    // 進むボタンアクション
    @IBAction func nextButton(sender: UIButton) {
        if pageControl.currentPage < numberOfPages {
            pageControl.currentPage++
            backButton.hidden = false
        }
        if pageControl.currentPage == numberOfPages-1 {
            nextButton.hidden = true
        }
        self.updateScrollViewContentOffset()
    }

    // pageControlのcurretPageValueが更新された時の処理
    @IBAction func pageChanged(sender: UIPageControl) {
        self.updateScrollViewContentOffset()
    }

    // 横スクロールのオフセットをページ開始に合わせる
    func updateScrollViewContentOffset() {
        scrollView.setContentOffset(
            CGPoint(
                x: scrollView.frame.size.width * CGFloat(pageControl.currentPage),
                y: 0.0
            ),
            animated: true
        )
    }

    func updateScrollViewContent() {
        // ページ数分の背景色を準備してみる
        let colors = [
            UIColor.redColor(),
            UIColor.blackColor(),
            UIColor.yellowColor(),
            UIColor.greenColor(),
            UIColor.purpleColor()
        ]
        for var i = 0; i < numberOfPages; i++ {
            let pageView = TodayView(i)
            let w = scrollView.frame.size.width
            let x = CGFloat(i) * w
            pageView.frame = CGRectMake(x, 0, w, viewHeight)
            pageView.backgroundColor = colors[i]
            scrollView.addSubview(pageView)
        }
    }
}

APIと繋いだTodayViewControllerのコード例

まだ綺麗にできると思うけど、こんな感じでAPIからデータを取得して表示させることができる。

TodayViewController.swift
import UIKit
import NotificationCenter


class TodayViewController: UIViewController, NCWidgetProviding {
    @IBOutlet weak var page1ViewButton: UIButton!
    @IBOutlet weak var page2ViewButton: UIButton!
    @IBOutlet weak var page3ViewButton: UIButton!
    @IBOutlet weak var page4ViewButton: UIButton!
    @IBOutlet weak var page5ViewButton: UIButton!

    @IBOutlet weak var pageControl: UIPageControl!
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var nextButton: UIButton!
    @IBOutlet weak var backButton: UIButton!

    @IBOutlet weak var page1View: UIView!
    @IBOutlet weak var page2View: UIView!
    @IBOutlet weak var page3View: UIView!
    @IBOutlet weak var page4View: UIView!
    @IBOutlet weak var page5View: UIView!

    @IBOutlet weak var page1Label: UILabel!
    @IBOutlet weak var page2Label: UILabel!
    @IBOutlet weak var page3Label: UILabel!
    @IBOutlet weak var page4Label: UILabel!
    @IBOutlet weak var page5Label: UILabel!

    @IBOutlet weak var page1ImageView: UIImageView!
    @IBOutlet weak var page2ImageView: UIImageView!
    @IBOutlet weak var page3ImageView: UIImageView!
    @IBOutlet weak var page4ImageView: UIImageView!
    @IBOutlet weak var page5ImageView: UIImageView!

    @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
    private var responseData: Array<Dictionary<String, AnyObject>>?
    private let numberOfPages: Int = 5 // ページ数
    private let viewHeight: CGFloat = 140 // 画面の高さ
    private let imageURLBase: String = "https://*******"
    private let imageURLSize: String = "********"
    private let endpoint : String = "https://**************"
    private var loading: Bool = false
    private var pageViews: [UIView] = []
    private var pageLabels: [UILabel] = []
    private var pageImageViews: [UIImageView] = []
    private var imageIds: [String] = []
    private var currentCountryCode: String = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        self.loading = false
        var locale = NSLocale.currentLocale()
        currentCountryCode = locale.objectForKey(NSLocaleCountryCode) as String
        pageViews = [
            page1View, page2View, page3View,
            page4View, page5View
        ]
        pageLabels = [
            page1Label, page2Label, page3Label,
            page4Label, page5Label
        ]
        pageImageViews = [
            page1ImageView, page2ImageView, page3ImageView,
            page4ImageView, page5ImageView
        ]

        // pageControlの準備
        pageControl.numberOfPages = numberOfPages

        // scrollViewの準備
        let scrollViewWidth = scrollView.frame.size.width
        scrollView.contentSize = CGSize(
            width: CGFloat(numberOfPages) * scrollViewWidth,
            height: viewHeight
        )
        for var i = 0; i < numberOfPages; i++ {
            pageViews[i].frame = CGRectMake(
                CGFloat(i) * scrollViewWidth, 0,
                scrollViewWidth, viewHeight
            )
            scrollView.addSubview(pageViews[i])
            pageImageViews[i].layer.cornerRadius = pageImageViews[i].frame.size.width/2
            pageImageViews[i].clipsToBounds = true
        }

        // Widgetに高さを明示的に教える
        self.preferredContentSize = CGSizeMake(0, viewHeight)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
        if self.loading == false {
            dispatch_async(dispatch_get_main_queue(), {
                self.activityIndicatorView.startAnimating()
            })
            self.loading = true
            let config = NSURLSessionConfiguration.defaultSessionConfiguration()
            let session = NSURLSession(configuration: config)
            var url = NSURL(string: self.endpoint)
            let task = session.dataTaskWithURL(url!) {
                (data, response, error) in
                var dict = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error:nil) as Dictionary<String, Array<Dictionary<String, AnyObject>>>;
                if error == nil && dict["root"] != nil {
                    self.responseData = dict["root"]?
                    if self.responseData?.count > 0 {
                        self.updateScrollViewContent()
                        completionHandler(NCUpdateResult.NewData)
                    } else {
                        completionHandler(NCUpdateResult.NoData)
                    }
                } else {
                    completionHandler(NCUpdateResult.Failed)
                }
                self.loading = false
            }
            task.resume()
        }
    }

    // 戻るボタンアクション
    @IBAction func backButton(sender: UIButton) {
        if pageControl.currentPage > 0 {
            pageControl.currentPage--
            nextButton.hidden = false
        }
        if pageControl.currentPage == 0 {
            backButton.hidden = true
        }
        self.updateScrollViewContentOffset()
    }

    // 進むボタンアクション
    @IBAction func nextButton(sender: UIButton) {
        if pageControl.currentPage < numberOfPages {
            pageControl.currentPage++
            backButton.hidden = false
        }
        if pageControl.currentPage == numberOfPages-1 {
            nextButton.hidden = true
        }
        self.updateScrollViewContentOffset()
    }

    // pageControlのcurretPageValueが更新された時の処理
    @IBAction func pageChanged(sender: UIPageControl) {
        self.updateScrollViewContentOffset()
    }

    @IBAction func openURL1(sender: UIButton) {
        self.openURL(0)
    }

    @IBAction func openURL2(sender: UIButton) {
        self.openURL(1)
    }

    @IBAction func openURL3(sender: UIButton) {
        self.openURL(2)
    }

    @IBAction func openURL4(sender: UIButton) {
        self.openURL(3)
    }

    @IBAction func openURL5(sender: UIButton) {
        self.openURL(4)
    }

    func openURL(index: Int) {
        var url = NSURL(string:"snapdish://dish?id=" + self.imageIds[index])
        self.extensionContext?.openURL(url!, completionHandler:{
            (success: Bool) -> Void in
        })
    }

    // 横スクロールのオフセットをページ開始に合わせる
    func updateScrollViewContentOffset() {
        scrollView.setContentOffset(
            CGPoint(
                x: scrollView.frame.size.width * CGFloat(pageControl.currentPage),
                y: 0.0
            ),
            animated: true
        )
    }

    // 画像を更新する
    func updateImageInScrollViewContent(index: Int, imageId: String?) {
        if let imageId = imageId? {
            self.imageIds.append(imageId)
            let url = NSURL(string: imageURLBase + imageId + imageURLSize);
            let req = NSURLRequest(URL: url!)
            NSURLConnection.sendAsynchronousRequest(req, queue:NSOperationQueue.mainQueue()){
                (res, data, error) in
                if error == nil {
                    self.pageImageViews[index].image = UIImage(data: data)
                } else {
                    self.pageImageViews[index].image = nil;
                }
            }
        } else {
            self.pageImageViews[index].image = nil;
        }
    }

    // scrollViewの内容を更新する
    func updateScrollViewContent() {
        dispatch_async(dispatch_get_main_queue(), { // main threadへ移動
            self.pageControl.numberOfPages = self.numberOfPages
            if let newNumberOfPages = self.responseData?.count {
                if newNumberOfPages < self.numberOfPages {
                    self.pageControl.numberOfPages = newNumberOfPages
                }
            }
            let w = self.scrollView.frame.size.width
            for i in 0..<self.numberOfPages {
                if self.pageControl.numberOfPages > i {
                    if let dict = self.responseData?[i] {
                        self.pageViews[i].hidden = false
                        self.pageLabels[i].text = dict["title"] as? String
                        if let imageId = dict["image_id"] as? Dictionary<String, String> {
                            self.updateImageInScrollViewContent(i, imageId: imageId["$oid"])
                        }
                    }
                } else {
                    self.pageViews[i].hidden = true
                }
            }
            self.nextButton.hidden = false
            self.activityIndicatorView.stopAnimating()
        })
    }
}

まとめ

今更だけどSwift書くとObjective-Cに戻れなくなりそう。直感的にサクサクかけてしまう。

15
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15