<TIL>
์ค๋์ ๋์์ ์ฑ ๋ง๋ค๊ธฐ ์ค์ต์ ํด๋ดค๋ค.
๋ฎ์๋ ๊ฐ์๋ณด๋ฉด์ ๊ณต๋ถํ๊ณ ,
์คํ์๋ ํด์ค๊ฐ์ ๋ณด๊ธฐ ์ ์ ๋จผ์ ๋ง๋ค์ด๋ดค๋ค.
4์์ Standard๋ฐ ์์
์ ๋ฃ๊ณ ,
์ ๋
ํ์ ์ ๊น์ง ๋์์ ์ฌ์ ์ฑ ๋ง๋ฌด๋ฆฌํ๋ ์๊ฐ์ ๊ฐ์ก๋ค.
(์คํ ๋ค๋๋ฐ ์ปฌ๋ ์
๋ทฐ ์ฐ์ต์ ๋ด์ผ ํ ์์ )
์ง๋ ํ ํ๋ก์ ํธ ๋,
๊ฒ์ ํ์ด์ง๋ฅผ ๋งก์ผ๋ฉด์ URLSession์ ์ฌ์ฉํ์ด์
ํฌ๊ฒ ์ด๋ ต์ง๋ ์์๋ ๊ฒ ๊ฐ๋ค.
(์ค๋์ ๋ฐ๋ณด ์ฐ ํ๋ ์์ ใ
.ใ
)
๋ด์ผ์ iOS ์ฑ ๊ฐ๋ฐ ์ฌํ ๊ฐ์ ์ ๋ฆฌํ๊ณ ,
์ค๋์ ๋์์ ์ฑ ๋ง๋ค๊ธฐ ๋ด์ฉ ์ ๋ฆฌ๋ฅผ ๊ฐ๋จํ๊ฒ ํด๋ณด๋ ค๊ณ ํ๋ค.
AVKit์ ์ฌ์ฉํด์
๋์์ ์ฌ์ ์ฑ ๋ง๋ค๊ธฐ
์ด๋ฒ ์ฌํ๊ฐ์ ๋ง๋ฌด๋ฆฌ ๊ณผ์ (?)
์๊ตฌ์ฌํญ์ README์ ์ ๋ฆฌํด๋ดค๋ค.
<github ์ฃผ์>
(https://github.com/yeggrrr/YeggrrrAVPlayerApp)
<์ค์ต์์ ์ฌ์ฉํ Dummy API ์ ๋ณด>
[GET]https://gist.githubusercontent.com/poudyalanil/ca84582cbeb4fc123a13290a586da925/raw/14a27bd0bcd0cd323b35ad79cf3b493dddf6216b/videos.json
< ์ฑ ๊ตฌํ ์ , info.plist ์ค์ >
info.plist - ‘App Transport Security Settings’ ํญ๋ชฉ ์์ฑ - ํ์์ ‘Allow Arbitrary Load’ ํญ๋ชฉ ์์ฑ ํ ‘Yes’๋ก ์ค์
< Model >
struct VideoInfo: Decodable {
let id: String
let title: String
let thumbnailUrl: URL
let duration: String
let uploadTime: String
let views: String
let author: String
let videoUrl: URL
let description: String
let subscriber: String
let isLive: Bool
}
VideoInfo ํ์ผ์ ํ๋ ์์ฑํด์
dummy API๋ก ํด๋น Response JSON์ ํ์ธํ์ฌ
๊ทธ ํ์์ ๋ง๊ฒ ๋ง๋ค์ด์ฃผ์๋ค.
(์์ฐ๋ ์ ๋ณด๋ค์ ๋นผ๋ ๋์ง๋ง, ์ฐ์ ๋ค ๋ฃ์)
< UI ์ค์ >
์์ง ์ฝ๋ ๋ฒ ์ด์ค๊ฐ ์ต์ํ์ง ์์์ ๋ง๋ฌด๋ฆฌ ๊ณผ์ ๋ ์คํ ๋ฆฌ๋ณด๋๋ฅผ ํ์ฉํ๋ค.
(์ด์ฐจํผ ์ด๋ฒ ๊ฐ์ธ ๊ณผ์ ๋ ์ฝ๋ ๋ฒ ์ด์ค..๐ฅน ํผํด๊ฐ ์ ์์)
Show Library์์
Table View & Table View Cell & Image View & Label
์ ์ถ๊ฐํด์ ์คํ ๋ ์ด์์์ ์ก์์ฃผ์๋ค.
(์ด์ ์คํ ๋ฆฌ๋ณด๋ ์คํ ๋ ์ด์์์ ๋๋ฆ..๋ง์คํฐ ํ์์ง๋..?๐)
< Outlet ์ฐ๊ฒฐ >
โ
TableViewCell ํ์ผ ์๋ก ์์ฑํด์ฃผ๊ณ ,
CustomClass์ Class ์ค์ ํด์ฃผ๊ธฐ!
Identifier๋ ์์ง๋ง๊ธฐ!
โก
TableViewCell ์ฝ๋์
ImageView, Label ์์ธ๋ ์ฐ๊ฒฐ
TableView๋ VC ์ฝ๋์ ์์ธ๋ ์ฐ๊ฒฐ!
โข
์ฝ๋์ static์ผ๋ก identifier ์ ์ฅํด์ฃผ๊ธฐ
static let identifier = "VideoTableViewCell"
(์ด๋ ๊ฒ ํด์ฃผ๋ฉด, ๋์ค์ ์ฐ๊ธฐ ํธํจ)
< Cell >
์ฐ์ , ์
์์ ์์ฑํด์ฃผ์ด์ผํ๋ ๊ฒ์
๋ก์ง์ ๊ตฌ์ฑํ๋ ์ธ๋ค์ผ ์
ํ
, ํ์ดํ ์
ํ
!
์ด ๋ ๊ฐ์ง ๋ฉ์๋๋ฅผ ๊ตฌํํ๋ฉด ๋๋ค.
func setThumbnail(imageURL: URL) {
DispatchQueue.global().async { [ weak self ] in
if let data = try? Data(contentsOf: imageURL), let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.thumbnailImage.image = image
}
} else {
DispatchQueue.main.async {
self?.thumbnailImage.image = UIImage(systemName: "video.fill")
self?.thumbnailImage.tintColor = .darkGray
}
}
}
}
์ด๋ ๊ฒ setThumbnail ํจ์๋ก imageURL์ ๋ฐ์์ค๊ณ ,
DispatchQueue๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ์ ์ผ๋ก ์์
ํด์ฃผ๊ธฐ!
๊ทธ๋ฆฌ๊ณ ์์ ๊ฐ์ด imageURL๋ฅผ ๋ฐ์ดํฐํ ํด์ฃผ๋ ์ฝ๋๋ฅผ ์์ฑํด์ฃผ๋ฉด ๋๋ค.
(๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ weak self ์ฌ์ฉ)
๊ทธ๋ฆฌ๊ณ ์์
์ด ์ ์ด๋ฃจ์ด์ก์ ๋์๋ main์์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๋๋๋ก ํด์ฃผ์๋ค.
(UI ์์
์ ๋ชจ๋ main์์ ํด์ค์ผํจ)
๋ฐํ์ด ๋์ง ์๊ณ , ์ด๋ฏธ์ง URL์ด ์๋ ๊ฒฝ์ฐ์๋
๋น ํ๋ฉด์ด ๋์ค์ง ์๋๋ก "Video.fill"์ด๋ผ๋ ๋ํดํธ ์ด๋ฏธ์ง๋ฅผ ๋ฃ์ด์ฃผ์๋ค.
func setTitle(title: String) {
self.titleLabel.text = title
}
override func prepareForReuse() {
super.prepareForReuse()
self.thumbnailImage.image = nil
}
๋ค์์ ํ์ดํ ์
ํ
ํด์ฃผ๊ธฐ!
setTitle ํจ์์์ title์ด๋ผ๋ ์ธ์๋ฅผ ๋ฐ์์ค๋๋ก ๋ง๋ค๊ณ ,
์ฐ๊ฒฐํด๋
ผ titleLabel์ text์ ๋ฃ์ด์ฃผ๊ธฐ!
๊ทธ๋ฆฌ๊ณ
prepareForReuse()๋ฅผ ์ฌ์ฉํด์ ์ฌ์ฌ์ฉ๋๋ ์
์ ์์ฑ์ ์ด๊ธฐํ ํด์ฃผ์๋ค.
(์คํ API๋ฅผ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๊ฒ ๋๋ฉด, ์ธํฐ๋ท ์ํฉ์ด ์ข์ง ์์ ๊ฒฝ์ฐ์ ๋น ๋ฅด๊ฒ ์คํฌ๋กค์ ํ์ ๋,
ํด๋น ์ด๋ฏธ์ง๊ฐ ์์ด์ผํ๋ ๊ณณ์ด ์๋ ๋ค๋ฅธ ์
์์ ๋ณด์ฌ์ง๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์ด๋ฐ ํ์์ ์
์ด ์ฌ์ฌ์ฉ์ด ๋๊ธฐ ๋๋ฌธ์ ์ด๋ฏธ์ง์ ํ
์คํธ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์์นํ์ง ์๋ ๊ฒ์ด๋ค!!
์ด๋ฐ ๋ฌธ์ ๋ฅผ ์์ ๊ฐ์ ์ฝ๋๋ก ํด๊ฒฐํ ์ ์๋ค.๐)
< VC >
video ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋ฆฌ์คํธ ๋ณ์๋ฅผ ์์ฑํด์คฌ๋ค.
var videoInfoList: [VideoInfo] = []
๊ทธ๋ฆฌ๊ณ ์์ ์คํ ๋ฆฌ๋ณด๋๋ก ๋ง๋ค์๋ ํ
์ด๋ธ ๋ทฐ์ ๋ํ ์ค์ ๋ถํฐ ํด์ฃผ์๋ค.
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videoInfoList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: VideoTableViewCell.identifier, for: indexPath) as? VideoTableViewCell else { return UITableViewCell() }
let product = self.videoInfoList[indexPath.row]
cell.setThumbnail(imageURL: product.thumbnailUrl)
cell.setTitle(title: product.title)
return cell
}
}
๊ทธ๋ฆฌ๊ณ viewDidLoad์์
videoListTableView.dataSource = self
videoListTableView.delegate = self
ํด์ฃผ๋ฉด ๋๋คใ
ใ
+
์ถ๊ฐ์ ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ๋๋ฆฌ๊ฒ ๋์ด์ค๋ ๊ฒฝ์ฐ์
์์ฐ์ค๋ฝ๊ฒ ๋ณด์ฌ์ง ์ ์๋๋ก
Kingfisher ํจํค์ง๋ฅผ ํ์ฉํด์ ์ถ๊ฐํด์คฌ๋ค.
let placeholderImage = UIImage(systemName: "video.fill")
cell.thumbnailImage.kf.setImage(with: product.thumbnailUrl, placeholder: placeholderImage)
cell.thumbnailImage.kf.indicatorType = .activity
cell.thumbnailImage.kf.setImage(with: product.thumbnailUrl,
options: [.transition(.fade(0.5)), .forceTransition, .keepCurrentImageWhileLoading])
๋ค์์ ๋ฐ์ดํฐ ๋ฐ์์ค๊ธฐ!!
(URLSession ํ์ฉ)
let urlString = "https://gist.githubusercontent.com/poudyalanil/ca84582cbeb4fc123a13290a586da925/raw/14a27bd0bcd0cd323b35ad79cf3b493dddf6216b/videos.json"
func getData() {
guard let url = URL(string: urlString) else { return }
let urlRequest = URLRequest(url: url)
URLSession.shared.dataTask(with: urlRequest) { data, request, error in
guard let data = data else { return }
do {
let decorder = JSONDecoder()
let result = try decorder.decode([VideoInfo].self, from: data)
self.videoInfoList = result
} catch {
print("error:\(error)")
}
}
.resume()
}
ใ
ใ
ใ
ใ
ใ
์ฌ๊ธฐ์ ๋ฑ์ฅํ๋ ๋ฐ๋ณด์ฐ..
guard let data = data else { return }
์์ print๋ก ๋ฐ์ดํฐ ์ ๋์ด์๋์ง ํ์ธํ๋๋ฐ..
์ ์ฐํ๊ธธ๋ '์ค.. ์ ๋์ด์ค๋๊ตฐ..'
๊ทธ๋ฌ๊ณ ์๋ฌด๋ฆฌ ์คํํด๋ ์๋จ๊ธธ๋ ๋ฉ๋ถ๐คฏ
์ฝ๋๋ฅผ ์ด๋ฆฌ์ ๋ฆฌ ๋ฐ๊ฟ๋ณด๊ณ '๋ด๊ฐ ์๋ชป ์๊ณ ์๋..? ์ด๊ฒ ์๋๊ฐ..?'
ํผ์ ๋๋ฆฌ๋๋ฆฌ ํ๋ค๊ฐ
ํํฐ๋ ์ฐพ์๊ฐ์ ์ด์ผ๊ธฐํ๋ค๊ฐ ๋ฐ๊ฒฌ๋..ใ
ใ
ใ
ใ
ใ
ใ
ใ
ใ
guard let data = data else { return }
๋ค์์ error ์์ผ๋ฉด ํ์ธํด์ผ์ง~ ํ๊ณ ์ผ๋
guard let error = error else { return }
์ด๋
์์ ์ง์ฐ์ง ์์์ ์๊ธด ๋ฌธ์ ...ใ
ใ
ใ
ใ
ใ
ใ
ใ
....
data๊ฐ ์ ๋์ด์์ผ๋, error๋ ์์ํ
๋ฐ
error๋ฅผ guard๋ฌธ์ผ๋ก ๋จ๊ฒจ๋์ ์๋ do catch ์ฝ๋๊ฐ ์คํ๋์ง ์์๋...๐
๋ด ๋์๋ง ๋ณด์ด์ง ์๋.. ์ฐพ์๊ฐ๋ฉด ๋ฐ๊ฒฌ๋๋..MAGIC..๐ช
์๋ฌดํผ!! ์ด๋ ๊ฒ ํ๊ณ !
tableView๋ฅผ reloadData๋ฅผ ํด์ค์ผํ๋
do๋ฌธ์์
DispatchQueue.main.async {
self.videoInfoList.reloadData()
}
ํด์ฃผ๊ฑฐ๋!!
var videoInfoList: [VideoInfo] = [] {
didSet {
DispatchQueue.main.async {
self.videoListTableView.reloadData()
}
}
}
์์ ๋ง๋ค์๋ videoInfoList์์ didSet์ผ๋ก reloadData ํด์ฃผ๊ธฐ!
์ด์ ๊ฑฐ์ ๋ง์ง๋ง ๋จ๊ณ!!
AVPlayerContoller ๋์ฐ๊ธฐ!
func presentAVPlayerViewController(videoURL: URL) {
let playerController = AVPlayerViewController()
let player = AVPlayer(url: videoURL as URL)
playerController.player = player
self.present(playerController, animated: true) {
player.play()
}
}
์ด๋ ๊ฒ ํด์ฃผ๊ธฐ!
(import AVKit ํด์ค์ผํจ)
๋ง์ง๋ง์ผ๋ก ํ
์ด๋ธ ๋ทฐ ์
์ด ์ ํ๋์๋์ ์ฝ๋๋ง ์์ฑํด์ฃผ๋ฉด ๋!!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let product = self.videoInfoList[indexPath.row]
presentAVPlayerViewController(videoURL: product.videoUrl)
}
์คํํด๋ณด๋ฉด!
์ง !โจ
(์ ์ฒด ์ฝ๋๋ ์์ github์ ์ฌ๋ ค๋์ต๋๋ค!)
'iOS ์ฑ ๊ฐ๋ฐ ์ข ํฉ๋ฐ > TIL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๊ฐ์ธ๊ณผ์ _์ฑ ๊ฒ์ ์ฑ ๋ง๋ค๊ธฐ_TIL (8) | 2024.05.07 |
---|---|
8์ฃผ์ฐจ_5์ผ์ฐจ_์ฑ ๊ฐ๋ฐ ์ฌํ_TodoList ์ฑ ์ฝ๋๋ก ๋ง๋ค๊ธฐ_TIL (0) | 2024.05.03 |
7์ฃผ์ฐจ_5์ผ์ฐจ_์ฑ ๊ฐ๋ฐ ์๋ จ_Closure(ํด๋ก์ )_๊ฐ์ธ๊ณต๋ถ_TIL (0) | 2024.04.19 |
7์ฃผ์ฐจ_4์ผ์ฐจ_์ฑ ๊ฐ๋ฐ ์๋ จ_๊ฐ์ธ๊ณต๋ถ_TIL (2) | 2024.04.18 |
7์ฃผ์ฐจ_2์ผ์ฐจ_์ฑ ๊ฐ๋ฐ ์๋ จ_ScrollView & Pull to Refresh_TIL (0) | 2024.04.16 |