Skip to content

Latest commit

History

192 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

🏫 학교 대항전

📖 목차

  1. 소개
  2. 개발환경 및 라이브러리
  3. Tree
  4. 키워드
  5. 타임라인
  6. 실행화면
  7. 트러블슈팅

🌱 소개

학교를 선택하고 게임화면으로 가서 화면을 터치하며 점수를 올리는 앱입니다.

🧑🏻‍💻 팀원

최동호황성진김성엽김혜란윤준성
Kakao-Talk-Photo-2023-09-19-15-30-111f

⚙️ 개발 환경 및 라이브러리

swiftxcodeFirebaseAlamofireGoogleMobileAds

🌲 Tree

📦TouchSchool
┣ 📂iOS
┃ ┣ 📂AD
┃ ┃ ┣ 📜BannerView.swift
┃ ┃ ┗ 📜BannerViewController.swift
┃ ┣ 📂Game
┃ ┃ ┣ 📜GameVM.swift
┃ ┃ ┗ 📜GameView.swift
┃ ┣ 📂Helpers
┃ ┃ ┣ 📂Font
┃ ┃ ┃ ┣ 📜Giants-Bold.otf
┃ ┃ ┃ ┗ 📜Recipekorea.ttf
┃ ┃ ┣ 📂Sound
┃ ┃ ┃ ┣ 📜buttomBGM.mp3
┃ ┃ ┃ ┣ 📜buttonBGM.mp3
┃ ┃ ┃ ┣ 📜errorBGM.mp3
┃ ┃ ┃ ┗ 📜mainBGM.mp3
┃ ┃ ┣ 📜ActivityIndicator.swift
┃ ┃ ┣ 📜Audio.swift
┃ ┃ ┣ 📜Colors.swift
┃ ┃ ┣ 📜Helpers.swift
┃ ┃ ┣ 📜infoView.swift
┃ ┃ ┣ 📜MultitouchRepresentable.swift
┃ ┃ ┗ 📜MultitouchView.swift
┃ ┣ 📂Main
┃ ┃ ┣ 📜MainVM.swift
┃ ┃ ┗ 📜MainView.swift
┃ ┣ 📂Model
┃ ┃ ┣ 📜School.swift
┃ ┃ ┗ 📜Smoke.swift
┃ ┣ 📂Rank
┃ ┃ ┗ 📜RankView.swift
┃ ┗ 📂Search
┃ ┃ ┣ 📜FirebaseManager.swift
┃ ┃ ┣ 📜SearchBar.swift
┃ ┃ ┣ 📜SearchGuide.swift
┃ ┃ ┣ 📜SearchVM.swift
┃ ┃ ┗ 📜SearchView.swift
┣ 📜ContentView.swift
┣ 📜GoogleService-Info.plist
┣ 📜Info.plist
┗ 📜TouchSchoolApp.swift

🔑 키워드

  • MVVM
  • URLSession
  • Alamofire
  • Firebase

⏰ 타임라인

Step 1 타임라인
  • 23.10.11 ~ 23.10.17
    • 프로젝트 시작
    • 학교검색화면 구현
    • 메인화면 구현
  • 23.10.19 ~ 23.10.26
    • 초,중,고등학교 데이터 가져와서 저장
    • URLSession -> Alamofire 라이브러리 적용
    • 학교정보 검색 시 필터링 기능 구현
Step 2 타임라인
  • 23.11.02 ~ 23.11.03
    • Firebase와 데이터 주고 받는 함수들 구현
    • 학교 선택 시 Firebase에 추가 및 데이터 연결
    • 배경화면 수정
    • 깃 컨벤션 템플릿 추가
  • 23.11.06 ~ 23.11.15
    • 랭킹 화면 추가
    • 게임 기능 구현 완료
    • 앱 실행 시 메인화면이 먼저나오도록 로직 수정
  • 23.11.16
    • 앱 종료 후 들어왔을 때 데이터 남게 수정
    • 터치시 이벤트 추가
Step 3 타임라인
  • 23.11.17
    • 비정상적인 값 검출 및 초기화 기능 구현
  • 23.11.19 ~ 23.11.21
    • UI 수정 및 sound데이터 추가
    • 게임 화면 터치 애니메이션 추가
  • 23.11.22
    • 메인BGM, 터치BGM, 오류BGM 추가
    • 게임화면 멀티터치 기능 구현
    • 앱 아이콘 생성
  • 23.11.23
    • 커스텀 폰트 적용
    • Sound 인스턴스 생성 후 재사용 로직으로 변경
    • 오디오 재생 백그라운드 스레드에서 처리

📱 실행 화면

앱 실행학교선택
게임화면랭킹화면

❓ 트러블 슈팅

Step1

GameView 멀티 터치가 안되던 이슈
  • GameView에서 화면을 터치할 때 여러 손가락으로 화면을 터치하면 먹히는 현상이 있었습니다.

  • SwiftUI는 직접적인 멀티터치 처리를 위한 API를 제공하지 않기에 기본적인 onTapGesture 대신에 더 낮은 수준의 이벤트 처리를 사용했습니다.

  • 멀티터치 기능을 활성화하기 위해 UIViewRepresentable프로토콜을 준수하는 MultitouchRepresentableUIView의 하위 클래스인 MultitouchView를 추가했습니다.

  • makeUIView(context:) 이 메소드는 MultitouchView 생성을 담당합니다. MultitouchViewtouchBegan 클로저를 설정합니다. 이 클로저는 MultitouchView에서 터치가 시작될 때마다 호출됩니다.

structMultitouchRepresentable:UIViewRepresentable{vartouchBegan:((CGPoint)->Void)func makeUIView(context:Context)->MultitouchView{letview=MultitouchView()
view.touchBegan = touchBegan
return view
}func updateUIView(_ uiView:MultitouchView, context:Context){}}
  • isMultipleTouchEnabled = true 이 코드를 통해 뷰가 여러 개의 동시 터치 이벤트를 감지할 수 있었습니다.
  • touchesBegan(_:with:) 이는 뷰에서 새로운 터치가 감지될 때마다 호출되는 UIView의 메서드를 재정의합니다. 이 메서드는 각 터치를 처리하고 터치 위치와 함께 touchBegan클로저를 호출합니다.
import UIKit
classMultitouchView:UIView{vartouchBegan:((CGPoint)->Void)?overrideinit(frame:CGRect){
super.init(frame: frame)
isMultipleTouchEnabled =true}requiredinit?(coder:NSCoder){fatalError("init(coder:) has not been implemented")}overridefunc touchesBegan(_ touches:Set<UITouch>, with event:UIEvent?){
touches.forEach{ touch inletlocation= touch.location(in:self)touchBegan?(location)}}}
  • 사용자가 화면을 터치할 때마다 MultitouchViewtouchesBegan(_:with:)가 호출되고, 이는 차례로 touchBegan클로저를 호출하게 됩니다.
  • 이 코드를 통해 여러 터치 이벤트를 동시에 감지하고 응답할 수 있게 해결했습니다.

Step 2

터치 효과음 관련 에러
  • SoundSetting 클래스에서 playSound메서드로 버튼을 클릭하면 효과음이 나오는 효과를 주려고했습니다.
  • playSound 메서드에서는 매번 새로운 `AVAudioPlayer 인스턴스를 생성하고 있었고, 이는 비효율적이며 성능 저하를 일으키고 있었습니다.
  • 또, playSound 메서드가 메인 스레드에서 오디오를 재생하여 화면이 뚝뚝 끊기는 문제가 있었습니다.
//변경 전
classSoundSetting:ObservableObject{staticletinstance=SoundSetting()varplayer:AVAudioPlayer?enumSoundOption:String{case mainBGM ="mainBGM"case buttonBGM ="buttonBGM"case errorBGM ="errorBGM"}func playSound(sound:SoundOption){guardlet url =Bundle.main.url(forResource: sound.rawValue, withExtension:".mp3")else{return}do{
player =tryAVAudioPlayer(contentsOf: url)
player?.play()
player?.volume =1}catch{print("재생하는데 오류가 발생했습니다. \(error.localizedDescription)")}}
  • 각 사운드별로 AVAudioPlayer 인스턴스를 사전에 생성하고 저장하는 방식으로 SoundSetting 클래스를 수정했습니다.
  • 또한, 오디오 재생을 백그라운드 스레드에서 수행하도록 변경했습니다.
// 수정 후
classSoundSetting:ObservableObject{staticletinstance=SoundSetting()privatevarplayers:[SoundOption:AVAudioPlayer]=[:]enumSoundOption:String,CaseIterable{case mainBGM ="mainBGM"case buttonBGM ="buttomBGM"case errorBGM ="errorBGM"}init(){forsoundinSoundOption.allCases {iflet url =Bundle.main.url(forResource: sound.rawValue, withExtension:"mp3"){do{letplayer=tryAVAudioPlayer(contentsOf: url)
player.prepareToPlay()players[sound]= player
}catch{print("오디오 플레이어 초기화 실패: \(error)")}}else{print("사운드 파일 로드 실패: \(sound.rawValue).mp3")}}}func playSound(sound:SoundOption){DispatchQueue.global().async{iflet player =self.players[sound], !player.isPlaying {
player.play()
player.volume =0.1}}}}

About

Team8 - 8

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages