Skip to content

Commit

Permalink
v2.0.2 (#88)
Browse files Browse the repository at this point in the history
* [Feat] 스탬프 뷰 수정

* [Chore] 주석 추가

* [Chore] 주석 제거

* [Refactor] UI 및 로직 수정

- Loading Indicator 추가
- UI 코드 수정

* App Logo

* App Logo

---------

Co-authored-by: yeni <dev.yeeun@gmail.com>
  • Loading branch information
insub4067 and yeniful authored Oct 1, 2023
1 parent 73ded43 commit eb4746e
Show file tree
Hide file tree
Showing 24 changed files with 60 additions and 233 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/20.png
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/29.png
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/40.png
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/50.png
Binary file not shown.
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/58.png
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/60.png
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/72.png
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/76.png
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/80.png
Binary file not shown.
Binary file removed AsyncSwift/Assets.xcassets/AppIcon.appiconset/87.png
Binary file not shown.
150 changes: 3 additions & 147 deletions AsyncSwift/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -1,153 +1,9 @@
{
"images" : [
{
"filename" : "40.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "60.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "29.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "58.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "87.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "80.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "120.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "57.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "57x57"
},
{
"filename" : "114.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "57x57"
},
{
"filename" : "120.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "180.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "20.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"filename" : "40.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "29.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "58.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "40.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"filename" : "80.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "50.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "50x50"
},
{
"filename" : "100.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "50x50"
},
{
"filename" : "72.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "72x72"
},
{
"filename" : "144.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "72x72"
},
{
"filename" : "76.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"filename" : "152.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"filename" : "167.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"filename" : "1024.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"filename" : "Logo.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion AsyncSwift/Models/Stamp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ struct Stamp: Decodable {
}

struct Card {

var originalPosition: CGFloat
var isSelected = false
var image: Image
var event: String
}
69 changes: 20 additions & 49 deletions AsyncSwift/Observed/StampView+Observed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import SwiftUI

extension StampView {
@MainActor final class Observed: ObservableObject {
@Published var cards = [String: Card]()
@Published var cards: [Card] = []
@Published var events = [String]()
@Published var currentIndex = 0
@Published var isLoading = true
private let keyChainManager = KeyChainManager()
private let cardInterval: CGFloat = (UIScreen.main.bounds.width - 32) * 56 / 358
private let cardSize: CGFloat = UIScreen.main.bounds.width - 32
Expand All @@ -22,8 +23,7 @@ extension StampView {

private func getEvents() -> [String] {
let pwRaw = keyChainManager.getItem(key: keyChainManager.stampKey) as? String
guard var convertedStringArray = pwRaw?.convertToStringArray() else { return [] }
convertedStringArray.insert("Next", at: 0) // MARK: Test 실제에서는 Next storage 둘다 설정해야함
guard let convertedStringArray = pwRaw?.convertToStringArray() else { return [] }
self.events = convertedStringArray.reversed()
return events
}
Expand All @@ -33,27 +33,33 @@ extension StampView {
private func fetchStampsImages(){
let events = getEvents()

guard !events.isEmpty else {
isLoading = false
return
}

events.enumerated().forEach { [weak self] in
guard let self = self else { return }
guard let self else { return }
let event = $0.element
let index = $0.offset
Task { @MainActor () -> Void in
guard let cardImageURL = URL(string: "https://raw.githubusercontent.com/Async-Swift/jsonstorage/main/Images/Stamp/" + event + "/stamp.png")
else { return }

let cardImageRequest = URLRequest(url: cardImageURL)
let (cardImageData, cardImageResponse) = try await URLSession.shared.data(for: cardImageRequest)
guard let httpsResponse = cardImageResponse as? HTTPURLResponse, httpsResponse.statusCode == 200 else { return }

guard let cardUIImage = UIImage(data: cardImageData) else { return }

self.cards[event] = Card(originalPosition: cardInterval * CGFloat(index),
image: Image(uiImage: cardUIImage))
// 가장 최근의 EventCard가 선택된 상태로 지정하기
if index == 0 {
self.cards[event]?.isSelected = true
} else {
self.cards[event]?.isSelected = false

let card = Card(
originalPosition: self.cardInterval * CGFloat(index),
image: Image(uiImage: cardUIImage),
event: event
)
self.cards.append(card)
if index == events.count - 1 {
self.isLoading = false
}
}
}
Expand Down Expand Up @@ -105,40 +111,5 @@ extension StampView {

return stamp
}

/// 가장 맨 위에 올라온 카드라면 아무것도 작동안하도록, 아니라면 가장 맨위로 올도록 하는 함수입니다.
func didCardTapped(index: Int, scrollReader: ScrollViewProxy) {
if index != currentIndex {
scrollReader.scrollTo(0, anchor: .init(x: 0, y: 94))
withAnimation(.spring()) {
cards[events[index]]?.isSelected = true
cards[events[currentIndex]]?.isSelected = false
currentIndex = index
}
}
}

/// 카드의 개수에 따라서 카드의 위치를 지정해주는 함수입니다.
func getCardOffsetY(index: Int, size: CGSize) -> CGFloat {
withAnimation(.spring()) {
guard let card = cards[events[index]] else { return .zero}
if card.isSelected {
return .zero
} else if size.height - CGFloat(94) < cardSize + CGFloat(16) + cardInterval * CGFloat(cards.count - 1) {
return cardSize + CGFloat(16) + card.originalPosition
} else {
return size.height - CGFloat(94) - cardInterval * CGFloat(cards.count - index) - CGFloat(8)
}
}
}

/// 스크롤을 원할하게 하기 위해서 Offset 으로 원래 크기 보다 밀려난 만큼 Spacer로 확보해줍니다.
func getSpacerMinLength(size: CGSize) -> CGFloat {
if size.height - CGFloat(94) < cardSize + CGFloat(16) + cardInterval * CGFloat(cards.count - 1) {
return cardSize + CGFloat(16) + cardInterval * CGFloat(cards.count - 1) + cardSize
} else {
return size.height - CGFloat(94) - cardInterval
}
}
}
}
71 changes: 35 additions & 36 deletions AsyncSwift/Views/StampView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,66 +10,65 @@ import SwiftUI
struct StampView: View {
@StateObject var observed = Observed()
@EnvironmentObject var envObserved: MainTabViewObserved
let columns = [
GridItem(.flexible(), spacing: 10),
GridItem(.flexible())
]

var body: some View {
GeometryReader { geometry in
if observed.cards.isEmpty {
emptyCardView
.padding(36)
} else {

GeometryReader { proxy in
NavigationView {
ScrollView(showsIndicators: false) {
ScrollViewReader { reader in
HStack(alignment: .bottom) {
Text("Stamp")
.font(.system(size: 34))
.fontWeight(.bold)
.padding(.leading, 16)
.padding(.bottom, 7)
.padding(.top, 48)
Spacer()
}
.frame(height: 94)
ZStack {
ForEach(0..<observed.cards.count, id: \.self) { index in
cardView(index: index, size: geometry.size, scrollReader: reader)
LazyVGrid(
columns: columns,
spacing: 10
) {
ForEach(observed.cards, id: \.event) { card in
cardView(card: card, size: proxy.size)
}
.padding(.horizontal, 16)
}
.padding(.horizontal, 14)
}
Spacer(minLength: observed.getSpacerMinLength(size: geometry.size))
}

}
}
.onOpenURL{ url in
Task {
if await observed.isAvailableURL(url: url) {
envObserved.currentTab = .stamp
.navigationTitle(Tab.stamp.title)
.overlay {
if observed.isLoading {
loadingIndicator
} else if !observed.isLoading, observed.cards.isEmpty {
emptyCardView
.padding(36)
}
}
}
}
}
}

private extension StampView {
var emptyCardView: some View {

@ViewBuilder var loadingIndicator: some View {
ProgressView()
.scaleEffect(1.5)
.padding(30)
.background(.ultraThinMaterial)
.cornerRadius(10)
}

@ViewBuilder var emptyCardView: some View {
ZStack {
RoundedRectangle(cornerRadius: 30)
.strokeBorder(Color(red: 0.78, green: 0.78, blue: 0.8), style: StrokeStyle(lineWidth: 2, dash: [10]))

Text("아직 참여한 행사가 없습니다.")
.foregroundColor(.gray)
}
}

func cardView(index: Int, size: CGSize, scrollReader: ScrollViewProxy) -> some View {
observed.cards[observed.events[index]]?.image
@ViewBuilder func cardView(card: Card, size: CGSize) -> some View {
card.image
.resizable()
.aspectRatio(contentMode: .fit)
.aspectRatio(contentMode: .fill)
.shadow(color: Color.black.opacity(0.1), radius: 10, y: 4)
.offset(y: observed.getCardOffsetY(index: index, size: size))
.onTapGesture {
observed.didCardTapped(index: index, scrollReader: scrollReader)
}
}
}

0 comments on commit eb4746e

Please sign in to comment.