diff --git a/Build.xcconfig b/Build.xcconfig index 8f96c14..9d34168 100644 --- a/Build.xcconfig +++ b/Build.xcconfig @@ -1,5 +1,5 @@ // アプリバージョンの整理番号 -APP_VERSION_CODE = 1 +APP_VERSION_CODE = 201 // アプリバージョンの文字表記 -APP_VERSION_NAME = 1.0 +APP_VERSION_NAME = 0.2.1 diff --git a/iOSEngineerCodeCheck.xcodeproj/project.pbxproj b/iOSEngineerCodeCheck.xcodeproj/project.pbxproj index 2d93f21..70a9f1a 100644 --- a/iOSEngineerCodeCheck.xcodeproj/project.pbxproj +++ b/iOSEngineerCodeCheck.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 05BC0E1A2B8B109B00FE3F12 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05BC0E192B8B109B00FE3F12 /* DetailViewController.swift */; }; + 05BC0E1D2B8B1A6300FE3F12 /* DetailViewParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05BC0E1C2B8B1A6300FE3F12 /* DetailViewParams.swift */; }; BF0A658D244F2A3B00280FA6 /* ViewController2.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0A658C244F2A3B00280FA6 /* ViewController2.swift */; }; BFD945DF244DC5E80012785A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD945DE244DC5E80012785A /* AppDelegate.swift */; }; BFD945E1244DC5E80012785A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD945E0244DC5E80012785A /* SceneDelegate.swift */; }; @@ -37,6 +39,8 @@ /* Begin PBXFileReference section */ 050D42562B8734DA00138A40 /* Build.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Build.xcconfig; sourceTree = ""; }; + 05BC0E192B8B109B00FE3F12 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; + 05BC0E1C2B8B1A6300FE3F12 /* DetailViewParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewParams.swift; sourceTree = ""; }; BF0A658C244F2A3B00280FA6 /* ViewController2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController2.swift; sourceTree = ""; }; BFD945DB244DC5E80012785A /* iOSEngineerCodeCheck.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSEngineerCodeCheck.app; sourceTree = BUILT_PRODUCTS_DIR; }; BFD945DE244DC5E80012785A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -79,6 +83,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 05BC0E182B8B0C1200FE3F12 /* Pages */ = { + isa = PBXGroup; + children = ( + 05BC0E1B2B8B1A4600FE3F12 /* Detail */, + ); + path = Pages; + sourceTree = ""; + }; + 05BC0E1B2B8B1A4600FE3F12 /* Detail */ = { + isa = PBXGroup; + children = ( + 05BC0E192B8B109B00FE3F12 /* DetailViewController.swift */, + 05BC0E1C2B8B1A6300FE3F12 /* DetailViewParams.swift */, + ); + path = Detail; + sourceTree = ""; + }; BFD945D2244DC5E80012785A = { isa = PBXGroup; children = ( @@ -103,6 +124,7 @@ BFD945DD244DC5E80012785A /* iOSEngineerCodeCheck */ = { isa = PBXGroup; children = ( + 05BC0E182B8B0C1200FE3F12 /* Pages */, BFD945DE244DC5E80012785A /* AppDelegate.swift */, BFD945E0244DC5E80012785A /* SceneDelegate.swift */, BFD945E2244DC5E80012785A /* ViewController.swift */, @@ -267,8 +289,10 @@ files = ( BFD945E3244DC5E80012785A /* ViewController.swift in Sources */, BF0A658D244F2A3B00280FA6 /* ViewController2.swift in Sources */, + 05BC0E1A2B8B109B00FE3F12 /* DetailViewController.swift in Sources */, BFD945DF244DC5E80012785A /* AppDelegate.swift in Sources */, BFD945E1244DC5E80012785A /* SceneDelegate.swift in Sources */, + 05BC0E1D2B8B1A6300FE3F12 /* DetailViewParams.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iOSEngineerCodeCheck/Base.lproj/Main.storyboard b/iOSEngineerCodeCheck/Base.lproj/Main.storyboard index 5654f27..810990f 100644 --- a/iOSEngineerCodeCheck/Base.lproj/Main.storyboard +++ b/iOSEngineerCodeCheck/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - - + + @@ -14,7 +14,7 @@ - + @@ -22,14 +22,14 @@ - + + + + + + diff --git a/iOSEngineerCodeCheck/Pages/Detail/DetailViewController.swift b/iOSEngineerCodeCheck/Pages/Detail/DetailViewController.swift new file mode 100644 index 0000000..b358b25 --- /dev/null +++ b/iOSEngineerCodeCheck/Pages/Detail/DetailViewController.swift @@ -0,0 +1,152 @@ +import UIKit + +/// 詳細画面 +/// +/// - Attention: UI をコードベースで実装しています +class DetailViewController : UIViewController { + + /// 詳細画面のViewController を生成する + /// + /// - Parameters: + /// - args: 画面遷移パラメータ + static func newInstance(_ args: DetailViewParams) -> UIViewController { + let vc = DetailViewController(args: args) + return vc + } + + + /// 画面遷移パラメータ + private let args: DetailViewParams + + /// フォーク数 + private lazy var viewForks = { + let view = UILabel() + view.text = "\(args.forksCount) forks" + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + /// アバター画像 + private lazy var viewImage = { + let view = UIImageView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + /// Issue 数 + private lazy var viewIssues = { + let view = UILabel() + view.text = "\(args.openIssuesCount) open issues" + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + /// プログラミング言語 + private lazy var viewLanguage = { + let view = UILabel() + view.text = "Written in \(args.language)" + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + /// スター数 + private lazy var viewStars = { + let view = UILabel() + view.text = "\(args.stargazersCount) stars" + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + /// タイトル(リポジトリ名) + private lazy var viewTitle = { + let view = UILabel() + view.text = args.fullName + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + /// ウォッチ数 + private lazy var viewWatchers = { + let view = UILabel() + view.text = "\(args.wachersCount) watchers" + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + + init(args: DetailViewParams) { + self.args = args + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("Always initialize DetailViewController using init(args:)") + } + + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + + let viewScroll = UIScrollView() + viewScroll.backgroundColor = .red // TODO + viewScroll.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(viewScroll) + + let viewBody = UIStackView() + viewBody.alignment = .fill + viewBody.axis = .vertical + viewBody.backgroundColor = .yellow // TODO + viewBody.translatesAutoresizingMaskIntoConstraints = false + viewScroll.addSubview(viewBody) + + if let avatarUrl = args.avatarUrl, let url = URL(string: avatarUrl) { + URLSession.shared.dataTask(with: url) { (data, res, err) in + let img = UIImage(data: data!)! + DispatchQueue.main.async { [weak self] in + self?.viewImage.image = img + } + }.resume() + } + viewBody.addArrangedSubview(viewImage) + + viewBody.addArrangedSubview(viewTitle) + + let viewColumns = UIStackView() + viewColumns.alignment = .top + viewColumns.axis = .horizontal + viewColumns.translatesAutoresizingMaskIntoConstraints = false + viewBody.addArrangedSubview(viewColumns) + + viewColumns.addArrangedSubview(viewLanguage) + + let viewStats = UIStackView() + viewStats.alignment = .trailing + viewStats.axis = .vertical + viewStats.translatesAutoresizingMaskIntoConstraints = false + viewColumns.addArrangedSubview(viewStats) + + viewStats.addArrangedSubview(viewStars) + viewStats.addArrangedSubview(viewWatchers) + viewStats.addArrangedSubview(viewForks) + viewStats.addArrangedSubview(viewIssues) + + NSLayoutConstraint.activate([ + viewScroll.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + viewScroll.leadingAnchor.constraint(equalTo: view.leadingAnchor), + viewScroll.trailingAnchor.constraint(equalTo: view.trailingAnchor), + viewScroll.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + + viewBody.topAnchor.constraint(equalTo: viewScroll.topAnchor), + viewBody.leadingAnchor.constraint(equalTo: view.leadingAnchor), + viewBody.trailingAnchor.constraint(equalTo: view.trailingAnchor), + viewBody.bottomAnchor.constraint(equalTo: viewScroll.bottomAnchor), + + viewImage.heightAnchor.constraint(equalTo: viewImage.widthAnchor), + viewImage.centerXAnchor.constraint(equalTo: viewBody.centerXAnchor), + + viewTitle.centerXAnchor.constraint(equalTo: viewBody.centerXAnchor), + ]) + } +} diff --git a/iOSEngineerCodeCheck/Pages/Detail/DetailViewParams.swift b/iOSEngineerCodeCheck/Pages/Detail/DetailViewParams.swift new file mode 100644 index 0000000..68630c8 --- /dev/null +++ b/iOSEngineerCodeCheck/Pages/Detail/DetailViewParams.swift @@ -0,0 +1,39 @@ +/// 詳細画面の遷移パラメータ +struct DetailViewParams { + + /// データの変換 + static func parse(_ item: [String: Any]) -> DetailViewParams { + let owner = item["owner"] as? [String: Any] + return DetailViewParams( + avatarUrl: owner?["avatar_url"] as? String, + forksCount: item["forks_count"] as? Int ?? 0, + fullName: item["full_name"] as? String, + language: item["language"] as? String ?? "", + openIssuesCount: item["open_issues_count"] as? Int ?? 0, + stargazersCount: item["stargazers_count"] as? Int ?? 0, + wachersCount: item["wachers_count"] as? Int ?? 0 + ) + } + + + /// アバター画像のURL + let avatarUrl: String? + + /// フォーク数 + let forksCount: Int + + /// 表示名 + let fullName: String? + + /// プログラミング言語 + let language: String + + /// 公開中のIssue の数 + let openIssuesCount: Int + + /// スター数 + let stargazersCount: Int + + /// ウォッチャー数 + let wachersCount: Int +} diff --git a/iOSEngineerCodeCheck/ViewController.swift b/iOSEngineerCodeCheck/ViewController.swift index 5dfabd1..a8ed7a9 100644 --- a/iOSEngineerCodeCheck/ViewController.swift +++ b/iOSEngineerCodeCheck/ViewController.swift @@ -85,8 +85,9 @@ class ViewController: UITableViewController, UISearchBarDelegate { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // 画面遷移時に呼ばれる idx = indexPath.row - performSegue(withIdentifier: "Detail", sender: self) - + let params = DetailViewParams.parse(repo[idx]) + let vcDetail = DetailViewController.newInstance(params) + navigationController?.pushViewController(vcDetail, animated: true) } }