参考链接:https://github.com/soapyigu/Swift-30-Projects
1 import UIKit 2 3 struct Work { 4 let title: String 5 let image: UIImage 6 let info: String 7 var isExpanded: Bool 8 }
1 import UIKit 2 3 struct Artist { 4 let name: String 5 let bio: String 6 let image: UIImage 7 var works: [Work] 8 9 init(name: String, bio: String, image: UIImage, works: [Work]) { 10 self.name = name 11 self.bio = bio 12 self.image = image 13 self.works = works 14 } 15 16 static func artistsFromBundle() -> [Artist] { 17 18 var artists = [Artist]() 19 20 guard let url = Bundle.main.url(forResource: "artists", withExtension: "json") else { 21 return artists 22 } 23 24 do { 25 let data = try Data.init(contentsOf: url) 26 guard let rootObject = try 27 JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else { 28 return artists 29 } 30 31 guard let artistObjects = rootObject["artists"] as? [[String: AnyObject]] else { 32 return artists 33 } 34 35 for artistObject in artistObjects { 36 if let name = artistObject["name"] as? String, 37 let bio = artistObject["bio"] as? String, 38 let imageName = artistObject["image"] as? String, 39 let image = UIImage.init(named: imageName), 40 let worksObject = artistObject["works"] as? [[String : String]] { 41 42 var works = [Work]() 43 44 for workObject in worksObject { 45 if let workTitle = workObject["title"], 46 let workImageName = workObject["image"], 47 let workImage = UIImage.init(named: workImageName + ".jpg"), 48 let info = workObject["info"] { 49 50 works.append(Work(title: workTitle, image: workImage, info: info, isExpanded: false)) 51 } 52 } 53 54 let artist = Artist(name: name, bio: bio, image: image, works: works) 55 artists.append(artist) 56 } 57 } 58 } catch { 59 return artists 60 } 61 62 return artists 63 } 64 }
1 import UIKit 2 3 class ArtistTableViewCell: UITableViewCell { 4 5 @IBOutlet weak var bioLabel: UILabel! 6 @IBOutlet weak var nameLabel: UILabel! 7 @IBOutlet weak var artistImageView: UIImageView! 8 9 override func awakeFromNib() { 10 super.awakeFromNib() 11 // Initialization code 12 } 13 14 override func setSelected(_ selected: Bool, animated: Bool) { 15 super.setSelected(selected, animated: animated) 16 17 // Configure the view for the selected state 18 } 19 20 }
1 import UIKit 2 3 class WorkTableViewCell: UITableViewCell { 4 5 @IBOutlet weak var workImageView: UIImageView! 6 @IBOutlet weak var workTitleLabel: UILabel! 7 @IBOutlet weak var moreInfoTextView: UITextView! 8 9 override func awakeFromNib() { 10 super.awakeFromNib() 11 // Initialization code 12 } 13 14 override func setSelected(_ selected: Bool, animated: Bool) { 15 super.setSelected(selected, animated: animated) 16 17 // Configure the view for the selected state 18 } 19 20 }
1 import UIKit 2 3 class ArtistListViewController: UIViewController { 4 5 let artists = Artist.artistsFromBundle() 6 7 @IBOutlet weak var tableView: UITableView! 8 9 override func viewDidLoad() { 10 super.viewDidLoad() 11 12 // Do any additional setup after loading the view. 13 14 tableView.rowHeight = UITableViewAutomaticDimension 15 tableView.estimatedRowHeight = 140 16 17 NotificationCenter.default.addObserver(forName: .UIContentSizeCategoryDidChange, object: .none, queue: OperationQueue.main) { [weak self] _ in 18 self?.tableView.reloadData() 19 } 20 } 21 22 override func didReceiveMemoryWarning() { 23 super.didReceiveMemoryWarning() 24 // Dispose of any resources that can be recreated. 25 } 26 27 // MARK: - Navigation 28 29 // In a storyboard-based application, you will often want to do a little preparation before navigation 30 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 31 // Get the new view controller using segue.destinationViewController. 32 // Pass the selected object to the new view controller. 33 if let destination = segue.destination as? ArtistDetailViewController, 34 let indexPath = tableView.indexPathForSelectedRow { 35 destination.selectedArtist = artists[indexPath.row] 36 } 37 } 38 39 } 40 41 extension ArtistListViewController: UITableViewDataSource { 42 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 43 return artists.count 44 } 45 46 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 47 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ArtistTableViewCell 48 49 let artist = artists[indexPath.row] 50 51 cell.bioLabel.text = artist.bio 52 cell.bioLabel.textColor = UIColor.init(white: 114 / 255, alpha: 1) 53 54 cell.artistImageView.image = artist.image 55 cell.nameLabel.text = artist.name 56 57 cell.nameLabel.backgroundColor = UIColor.init(red: 1, green: 152 / 255, blue: 0, alpha: 1) 58 cell.nameLabel.textColor = UIColor.white 59 cell.nameLabel.textAlignment = .center 60 cell.selectionStyle = .none 61 62 cell.nameLabel.font = UIFont.preferredFont(forTextStyle: .headline) 63 cell.bioLabel.font = UIFont.preferredFont(forTextStyle: .body) 64 65 return cell 66 } 67 }
1 import UIKit 2 3 class ArtistDetailViewController: UIViewController { 4 5 var selectedArtist: Artist! 6 7 let moreInfoText = "Select For More info >" 8 9 @IBOutlet weak var tableView: UITableView! 10 11 override func viewDidLoad() { 12 super.viewDidLoad() 13 14 // Do any additional setup after loading the view. 15 16 title = selectedArtist.name 17 18 tableView.rowHeight = UITableViewAutomaticDimension 19 tableView.estimatedRowHeight = 300 20 21 NotificationCenter.default.addObserver(forName: .UIContentSizeCategoryDidChange, object: .none, queue: OperationQueue.main) { [weak self] _ in 22 self?.tableView.reloadData() 23 } 24 } 25 26 override func didReceiveMemoryWarning() { 27 super.didReceiveMemoryWarning() 28 // Dispose of any resources that can be recreated. 29 } 30 31 32 /* 33 // MARK: - Navigation 34 35 // In a storyboard-based application, you will often want to do a little preparation before navigation 36 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 37 // Get the new view controller using segue.destinationViewController. 38 // Pass the selected object to the new view controller. 39 } 40 */ 41 42 } 43 44 extension ArtistDetailViewController: UITableViewDataSource { 45 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 46 return selectedArtist.works.count 47 } 48 49 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 50 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! WorkTableViewCell 51 52 let work = selectedArtist.works[indexPath.row] 53 54 cell.workTitleLabel.text = work.title 55 cell.workImageView.image = work.image 56 57 cell.workTitleLabel.backgroundColor = UIColor.init(white: 204 / 255, alpha: 1) 58 cell.workTitleLabel.textAlignment = .center 59 cell.moreInfoTextView.textColor = UIColor.init(white: 114 / 255, alpha: 1) 60 cell.selectionStyle = .none 61 62 cell.moreInfoTextView.text = work.isExpanded ? work.info : moreInfoText 63 cell.moreInfoTextView.textAlignment = work.isExpanded ? .left : .center 64 65 cell.workTitleLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline) 66 cell.moreInfoTextView.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.footnote) 67 68 return cell 69 } 70 } 71 72 extension ArtistDetailViewController: UITableViewDelegate { 73 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 74 guard let cell = tableView.cellForRow(at: indexPath) as? WorkTableViewCell else { 75 return 76 } 77 78 var work = selectedArtist.works[indexPath.row] 79 80 work.isExpanded = !work.isExpanded 81 selectedArtist.works[indexPath.row] = work 82 83 cell.moreInfoTextView.text = work.isExpanded ? work.info : moreInfoText 84 cell.moreInfoTextView.textAlignment = work.isExpanded ? .left : .center 85 86 tableView.beginUpdates() 87 tableView.endUpdates() 88 89 tableView.scrollToRow(at: indexPath, at: .top, animated: true) 90 91 } 92 }