묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[Lv.2] SwiftUI 중급 - Core Data와 SwiftData로 앱 완성하기
Apple 공식 문서 보는법
안녕하세요 좋은 강의 잘 듣고 있습니다.다름이 아니라 혹시 애플 공식 문서보는 강사님만의 꿀팁 같은게 있을까요?공식문서를 찾아볼 수 있긴 한데 찾고자 하는걸 정확하게 검색해야 결과를 얻을 수 있고 뭔가 보기가 어렵게 되어 있는 것 같아서요...그리고 제가 원하는 기능을 구현하려면 물론 구글링도 있지만 공식문서를 최대한 참고해서 구현 방법을 익히고 싶은데 이런 것들은 공식 문서에서 어떻게 검색해야 하나요?예를 들어 SwiftUI에서 반복문으로 이루어진 텍스트들을 한번에 지우는 기능을 구현하고 싶은데 이에 연관된 것들을 공식 문서에서 쉽게 찾는 방법 등을 알고 싶습니다.감사합니다.
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
Todo 앱 @Observable 질문입니다
안녕하세요 @Observable 이 부분에서 에러가 발생해서 질문드립니다현재 맥북 버전은 Sequoia 15.0이며 Xcode 버전은 16.0을 사용 중입니다 Observable()' is only available in iOS 17.0 or newer라는 에러가 발생한 상황입니다 구글링을 해도 해결방법을 찾지 못해서 질문드립니다..! 그리고 추가적으로 Xcode 16으로 업뎃 후 프리뷰 화면 보여주는 단축키인 cmd+option+enter를 아무리 눌러도 프리뷰가 xcode에서 보이지 않는 상황입니다.. 마지막으로 제가 질문드렸던 다른 게시글인데 확인 한 번 부탁드리겠습니다VStack, HStack으로 뷰 구성하기 강의 padding 질... - 인프런 | 커뮤니티 질문&답변 (inflearn.com)답변 주시면 감사하겠습니다!
-
미해결앱 개발 기초부터 실전까지 하나로 끝내는 Swift & iOS 마스터 클래스
파라미터와 아규먼트 혼동
안녕하세요! 좋은 강의 잘 수강하고 있습니다.[계산기 #6 - 경고! 값을 입력하세요!] 강의에서19분 4초에 함수 호출 방법 설명이 나와있는데 함수 정의에 parameter라고 하고,함수 호출시에는 argument라고 하는 걸로 알고 있습니다. 잘못 표기된 걸까요..?아규먼트 레이블 같은 개념과도 혼동될수 있을 것 같아서요
-
미해결성공적인 진짜 iOS 개발자 되기 [기초부터 실무까지]
에러 질문 [UITextField setDataSource:]: unrecognized selector sent to instance 0x107079600"
강의 수강 중 35분 정도까지 따라한 뒤 실행했는데, 다음 에러가 발생했는데 이유를 잘 모르겠습니다. 다음처럼 에러가 발생하였는데요. 에러 내용과 작성한 코드들 첨부드립니다. Exception NSException * "-[UITextField setDataSource:]: unrecognized selector sent to instance 0x107079600" 0x0000600000c9d170 에러 전체 내용 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITextField setDataSource:]: unrecognized selector sent to instance 0x107079600' *** First throw call stack: ( 0 CoreFoundation 0x00000001804b70ec __exceptionPreprocess + 172 1 libobjc.A.dylib 0x000000018008ede8 objc_exception_throw + 72 2 CoreFoundation 0x00000001804ccc54 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0 3 UIKitCore 0x0000000185ace7a8 -[UIResponder doesNotRecognizeSelector:] + 232 4 CoreFoundation 0x00000001804bb3ec ___forwarding___ + 1268 5 CoreFoundation 0x00000001804bdb0c _CF_forwarding_prep_0 + 92 6 CurrencyConverterApp.debug.dylib 0x000000010508d1ec $s20CurrencyConverterApp20PickerViewControllerC11viewDidLoadyyF + 648 7 CurrencyConverterApp.debug.dylib 0x000000010508d270 $s20CurrencyConverterApp20PickerViewControllerC11viewDidLoadyyFTo + 36 8 UIKitCore 0x000000018533369c -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 80 9 UIKitCore 0x0000000185338238 -[UIViewController loadViewIfRequired] + 908 10 UIKitCore 0x000000018529db34 -[UINavigationController _ensureToViewControllersViewIsLoaded:] + 84 11 UIKitCore 0x000000018529df70 -[UINavigationController transitionConductor:willTransitionFromViewController:toViewController:] + 184 12 UIKitCore 0x0000000185c35c70 -[_UIViewControllerTransitionConductor startDeferredTransitionIfNeeded] + 452 13 UIKitCore 0x00000001852951a4 -[UINavigationController __viewWillLayoutSubviews] + 80 14 UIKitCore 0x000000018527cf58 -[UILayoutContainerView layoutSubviews] + 168 15 UIKitCore 0x000000018601c0c4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2404 16 QuartzCore 0x000000018b06ceb0 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 432 17 QuartzCore 0x000000018b077c34 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 124 18 QuartzCore 0x000000018afacc58 _ZN2CA7Context18commit_transactionEPNS_11TransactionEdPd + 464 19 QuartzCore 0x000000018afdb468 _ZN2CA11Transaction6commitEv + 652 20 UIKitCore 0x0000000185abb7b4 __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 32 21 CoreFoundation 0x000000018041b0ec __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20 22 CoreFoundation 0x000000018041a824 __CFRunLoopDoBlocks + 352 23 CoreFoundation 0x00000001804150c8 __CFRunLoopRun + 812 24 CoreFoundation 0x0000000180414960 CFRunLoopRunSpecific + 536 25 GraphicsServices 0x0000000190183b10 GSEventRunModal + 160 26 UIKitCore 0x0000000185aa2b40 -[UIApplication _run] + 796 27 UIKitCore 0x0000000185aa6d38 UIApplicationMain + 124 28 UIKitCore 0x0000000184e9a184 block_destroy_helper.22 + 9660 29 CurrencyConverterApp.debug.dylib 0x000000010508820c $sSo21UIApplicationDelegateP5UIKitE4mainyyFZ + 120 30 CurrencyConverterApp.debug.dylib 0x0000000105088184 $s20CurrencyConverterApp0C8DelegateC5$mainyyFZ + 44 31 CurrencyConverterApp.debug.dylib 0x0000000105088288 __debug_main_executable_dylib_entry_point + 28 32 dyld 0x00000001050dd410 start_sim + 20 33 ??? 0x0000000104e1e274 0x0 + 4376879732 ) libc++abi: terminating due to uncaught exception of type NSException AppDelegate.swift// // AppDelegate.swift // CurrencyConverterApp // // Created by hojeongpark on 9/24/24. // import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } } PickerViewController.swift// // ViewController.swift // CurrencyConverterApp // // Created by hojeongpark on 9/24/24. // import UIKit class PickerViewController: UIViewController { var rates: [(String, Double)]? @IBOutlet weak var currencyPicker: UIPickerView! override func viewDidLoad() { super.viewDidLoad() self.navigationItem.title = "Currency List" currencyPicker.delegate = self currencyPicker.dataSource = self fetchJson() } func fetchJson() { let urlString = "https://open.er-api.com/v6/latest/USD" guard let url = URL(string: urlString) else { return } print("fetchJson") // data task URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data else { return } do { let currencyModel = try JSONDecoder().decode(CurrencyModel.self, from: data) self.rates = currencyModel.rates?.sorted{ $0.key < $1.key } // 피커 새로고침 해야 데이터 내용이 보입니다. DispatchQueue.main.async { self.currencyPicker.reloadAllComponents() } print("currencyModel", currencyModel) }catch{ print("error", error) } }.resume() } } extension PickerViewController: UIPickerViewDelegate, UIPickerViewDataSource { func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return rates?.count ?? 0 } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return rates?[row].0 } }
-
미해결성공적인 진짜 iOS 개발자 되기 [기초부터 실무까지]
Main interface 지정 방법
안녕하세요. 강의 중에 설명해주시는 내용 중 Development info > Main interface 설정하는 부분이 안보여서 질문드립니다. 최신 xcode, mac 버전을 사용하고 있어 업데이트가 된 거 같은데 어디에서 설정할 수 있을까요? 강의 중 화면 제 화면.
-
미해결앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
(기초-3)가위바위보 앱 만들기 - 기초 앱 강의 12강 질문입니다!
Build 시 계속 위의 에러가 납니다. 그런데 Allen 님께 다운 로드 받은 파일을 해보면 문제가 없습니다. 부탁 드립니다.
-
미해결성공적인 진짜 iOS 개발자 되기 [기초부터 실무까지]
Closure_3 Capture List에 참조 타입 값을 작성할 경우에도 deinit 실행되는 이유가 뭘까요?
import UIKit class TestClass {} class SomeClass { var a = 10 var b = TestClass() func myFunc() { print(self.a) } lazy var myClosure1: (()->Void)? = { () -> Void in print(self.a) } lazy var myClosure2 = { [a] () -> Void in print(a) } lazy var myClosure3 = { [b] in print(b) } deinit { print("SomeClass deinit") } } var myClass1: SomeClass? = SomeClass() var myClass2 = myClass1 var myClass3 = myClass1 var myClass4 = myClass1 myClass1?.myClosure1 myClass1?.myClosure1 = nil myClass1?.myClosure2 myClass1?.myClosure3 myClass1 = nil myClass2 = nil myClass3 = nil myClass4 = nil위 코드에서 myClusore3 의 캡쳐 리스트엔 b를 작성했고, b는 클래스의 인스턴스로 참조타입으로 알고 있습니다.이 때 위 코드를 실행하면 deinit이 실행되는데요. weak 키워드를 사용하지 않았는데도 deinit이 실행되는 이유가 있을까요?
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
VStack, HStack으로 뷰 구성하기 강의 padding 질문입니다
안녕하세요 강의 수강 중 이해가 안되는 부분이 있어서 질문 드립니다 import SwiftUI struct ContentView: View { var body: some View { VStack{ HStack{ Image("dogProfile") .resizable() .frame(width: 120, height: 120) .clipShape(Circle()) .overlay( Circle() .stroke(Color.teal, lineWidth: 3) ) VStack(alignment: .leading){ Text("이름: 키도") Text("시바견") Text("2017년 5월생") } // VStack 안에 있는 뷰에 모디파어어를 일괄 적용하고 싶을 경우 이런 식으로 구성할 수 있다(일괄 적용이라는 표현보단 Vstack 자체에 모디파이어를 적용했다는 표현이 더 옳을지도? .font(.system(size: 20)) // 정보 글의 폰트 사이즈 .padding(.leading, 20) // 그림과 정보 글 사이의 공백 } .padding(.bottom, 20) VStack(alignment: .leading, spacing: 10){ Text("좋아하는 것") .font(.system(size: 20)) .bold() Divider() // 좋아하는 것 밑에 있는 선을 그려주는 함수 Text("- 각종 고기(🐮 = 🐷 = 🐔 > 🐟)") Text("- 장본 게 담겨 있는 종량제 봉투") Text("- 집에 새로 온 사람") } .padding() .background( RoundedRectangle(cornerRadius: 15) .fill(.mint) .opacity(0.2) // 색의 투명도를 지정하는 모디파이어 0이면 완전 투명 .shadow(radius: 5) ) VStack(alignment: .leading, spacing: 10){ Text("스킬") .font(.system(size: 20)) .bold() Divider() // 좋아하는 것 밑에 있는 선을 그려주는 함수 HStack(alignment: .top, spacing: 30){ VStack(alignment: .leading, spacing: 10){ Text("- 앉아") Text("- 엎드려(앉아와 자주 혼동함)") Text("- 하우스(집으로 들어가기)") } VStack(alignment: .leading, spacing: 10){ Text("- 손") Text("- 코") } } } .padding() .background( RoundedRectangle(cornerRadius: 15) .fill(.pink) .opacity(0.2) // 색의 투명도를 지정하는 모디파이어 0이면 완전 투명 .shadow(radius: 5) ) } .padding() // 질문인 부분 } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } }위 코드 맨 마지막 padding을 보면 가장 처음 사용하는 VStack에 .padding을 적용하는 코드입니다 궁금한 건 저 padding의 값을 50, 100 이렇게 설정해도 Vstack의 위 아래 여백은 변하지 않는다는 점입니다. 분명 .padding에 값을 넣어주지 않으면 기본적으로 상하좌우 모든 부분에 여백을 주는 걸로 알고 있는데 만 상하 부분의 여백이 변하지 않는 것을 확인했습니다. 개인적으로 여백을 넓게 잡으면 여백을 잡기 위해 글자나 사진 사이즈 등이 뭉개지는 현상이 발생할 줄 알았는데 좌우는 뭉개지는 데 상하는 뭉개지지 않더라구요 Vstack이 기본적으로 가지고 있는 공간이 있는 최소한의 건지... 혹시 뭉개지지 않는 이유를 알 수 있을까요?? 그리고 혹시 // VStack 안에 있는 뷰에 모디파어어를 일괄 적용하고 싶을 경우 이런 식으로 구성할 수 있다(일괄 적용이라는 표현보단 Vstack 자체에 모디파이어를 적용했다는 표현이 더 옳을지도? 이 주석 제가 맞게 이해한 건지도 알려주시면 정말 감사하겠습니다..! 좋은 하루 보내세요! 감사합니다!
-
미해결간단하게 만들어 보는 iOS 설정앱
@ViewBuilder 사용 질문입니다
안녕하세요! 다름이 아니라 강의를 보면 @ViewBuilder 어노테이션을 사용하시는데 강사님이 쓰신 블로그인지는 모르겠지만https://dev200ok.blogspot.com/2022/05/swiftui.html(블로그 주인 이름이 강사님과 동일)위 링크에 따르면SwiftUI에서 UI를 그리다 보면, 뷰를 따로 떼어내서 작업을 해야하는 경우들이 발생@ViewBuilder 어노테이션을 붙여 뷰를 만들어 주는 메소드를 만들어 줄 수 있습니다. 뷰를 코드와 분리하기 때문에 코드의 가독성을 높여주며 다른 뷰를 독립적으로 보기 좋음@ViewBuilder를 붙여서 만든다면 암시적으로 해당 뷰 안에서만들어 사용하겠다는 뜻위와 같은 이유로 @ViewBuilder를 사용한다고 되어 있습니다 근데 제 코드를 보시면import SwiftUI struct ContentView: View { @State private var airplaneMode: Bool = false func Profile() -> some View{ NavigationLink{ Text("프로필 화면") } label: { HStack{ Image(systemName: "person.fill") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 40, height: 40) .padding(.all, 10) .background(.green) .clipShape(Circle()) VStack(alignment: .leading, spacing: 3){ Text("홍길동") .font(.system(size: 24)) .fontWeight(.regular) Text("Apple ID, iCloud, 미디어 및 구입") .font(.system(size: 14)) } .padding(.leading, 6) } .padding(.vertical, 10) } } func ImageDesign(sysName: String, imgColor: Color) -> some View{ Image(systemName: sysName) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 20, height: 20) .padding(.all, 4) .background(imgColor) .foregroundColor(.white) .cornerRadius(6) } func ListMenuLetter(sysName: String, NaviLinkText: String, imgColor: Color) -> some View{ HStack{ NavigationLink{ switch NaviLinkText{ case "WI-FI": Text("WI-FI 화면") case "Bluetooth": Text("Bluetooth 화면") default: EmptyView() // 빈 뷰를 반환 } } label: { switch NaviLinkText{ case "WI-FI": ImageDesign(sysName: sysName, imgColor: imgColor) HStack{ Text("WI-FI") Spacer() Text("IPTIME") .foregroundColor(.gray) } case "Bluetooth": ImageDesign(sysName: sysName, imgColor: imgColor) HStack{ Text("Bluetooth") Spacer() Text("켬") .foregroundColor(.gray) } default: EmptyView() // 빈 뷰를 반환 } } } } func ListMenu(sysName: String, NaviLinkText: String, imgColor: Color) -> some View{ HStack{ ImageDesign(sysName: sysName, imgColor: imgColor) if NaviLinkText != "에어플레인 모드"{ NavigationLink(NaviLinkText){ // 여기에 지금 label이 따로 지정되어 있지 않은데 이럴 경우 괄호 안에 있는 NaviLinkText가 label 역할을 함 switch NaviLinkText{ case "셀룰러": Text("셀룰러 화면") case "개인용 핫스팟": Text("개인용 핫스팟 화면") case "스크린 타임": Text("스크린 타임 화면") case "일반": Text("일반 화면") case "손쉬운 사용": Text("손쉬운 사용 화면") case "개인 정보 보호": Text("개인 정보 보호 화면") case "암호": Text("암호 화면") default: EmptyView() // 빈 뷰를 반환 } } } else{ Toggle("에어플레인 모드", isOn: $airplaneMode) } } } var body: some View { NavigationView { List{ Section{ Profile() } Section{ ListMenu(sysName: "airplane", NaviLinkText: "에어플레인 모드", imgColor: .orange) ListMenuLetter(sysName: "wifi", NaviLinkText: "WI-FI", imgColor: .blue) ListMenuLetter(sysName: "b.circle", NaviLinkText: "Bluetooth", imgColor: .blue) ListMenu(sysName: "antenna.radiowaves.left.and.right", NaviLinkText: "셀룰러", imgColor: .green) ListMenu(sysName: "personalhotspot", NaviLinkText: "개인용 핫스팟", imgColor: .green) } Section{ ListMenu(sysName: "hourglass", NaviLinkText: "스크린 타임", imgColor: .blue) } Section{ ListMenu(sysName: "gear", NaviLinkText: "일반", imgColor: .gray) ListMenu(sysName: "person.crop.circle", NaviLinkText: "손쉬운 사용", imgColor: .blue) ListMenu(sysName: "hand.raised.fill", NaviLinkText: "개인 정보 보호", imgColor: .blue) } Section{ ListMenu(sysName: "key.fill", NaviLinkText: "암호", imgColor: .gray) } } .navigationTitle(Text("설정")) // 타이틀 위치 코드가 맨 위가 아닌데 정상 동작하는 이유는 무엇인가 } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }Profile 함수나 ImageDesign 등 뷰를 함수로 따로 빼서 독립적인 코드를 구성했는데 @ViewBuilder를 사용하지 않고도 아무런 문제가 없이 정상적으로 동작합니다. 사용하지 않아도 문제가 없는데 강의에서 @ViewBulider를 사용하시는 이유가 궁금합니다 답변 주시면 감사하겠습니다!
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
@State 특성(?) 질문입니다
import SwiftUI struct ContentView: View { @State private var ToggleCheck: Bool = false // @State 없으면 에러 왜? / 연산자 토글을 누른 후의 숫자 입력인지 체크하기 위한 변수 @State private var isAdditionActive = false // 덧셈 토글 체크 @State private var isSubtractionActive = false // 뺄셈 토글 체크 @State private var isMultiplicationActive = false // 곱셈 토글 체크 @State private var isDivisionActive = false // 나눗셈 토글 체크 @State private var printNumber: String = "0" // 계산식에 사용 or 화면에 출력한 내용을 담은 변수 @State private var totalNumber: Int = 0 // 토탈 계산 값 @State private var op: Character = " " @State private var buttonData: [[String]] = [ // 키패드 모음 2차원 배열 ["AC", "+/-", "%", "÷"], ["7", "8", "9", "X"], ["4", "5", "6", "-"], ["1", "2", "3", "+"], ["0", ".", "="]] var body: some View { ZStack{ Color.black.ignoresSafeArea() // 화면 전체 여백 없이 배경색 지정 VStack { Spacer() HStack{ Spacer() Text("\(Int(printNumber)!)") // 계산기의 숫자 출력 .padding() .font(.system(size: 73)) .foregroundColor(.white) } ButtonPosition() // C, +/-, %를 제외한 버튼을 화면에 추가 } } } func Reset(){ printNumber = "0" // 출력된 계산 숫자 UI 초기화 totalNumber = 0 // 연산의 토탈값을 담고 있는 변수도 당연히 초기화 ToggleCheck = false op = " " buttonData[0][0] = "AC" isAllfalse() // 버튼의 토글값 및 연산자 버튼 토글 UI도 모두 초기화 } func isAllfalse(){ // 연산자의 토글 버튼을 전부 해제 하기 위한 함수 isAdditionActive = false isSubtractionActive = false isMultiplicationActive = false isDivisionActive = false } func opIfSwitch(){ // 실질적인 계산을 위한 switch문 if op == " "{ totalNumber = Int(printNumber)! } else{ switch op{ case "÷": totalNumber /= Int(printNumber)! if printNumber == "0"{ // 비정상 종료.. 왜? printNumber = "오류" } case "X": totalNumber *= Int(printNumber)! case "-": totalNumber -= Int(printNumber)! case "+": totalNumber += Int(printNumber)! default: break } } printNumber = String(totalNumber) } // 내 코드의 계산 방식 작성할 것 이따가 func Division(){ // 나눗셈 opIfSwitch() } func Multiplication(){ // 곱셈 opIfSwitch() } func Minus(){ // 뺄셈 opIfSwitch() } func Plus(){ // 덧셈 opIfSwitch() } func Equal(){ // 등호 opIfSwitch() isAllfalse() ToggleCheck = false // =을 누른 후 결과 값에 추가로 연산을 진행할 때 이 코드 없으면 정상적으로 진행되지 않음 } func ButtonPosition() -> some View{ // 버튼 디자인 및 포지셔닝 함수 ForEach(buttonData, id: \.self){ line in HStack{ ForEach(line, id: \.self){ row in switch row{ case "AC", "C", "+/-", "%": ButtonDesign(row, .gray, .black) case "÷", "X", "-", "+", "=": ButtonDesign(row, .orange, .white) default: ButtonDesign(row, .init("NumberButton"), .white) } } } } } func ButtonDesign(_ value: String, _ backcolor: Color, _ fcolor: Color) -> some View{ // 공통 버튼 디자인 Button { switch value { case "÷": isDivisionActive = true // 나눗셈 버튼 토글 isAdditionActive = false isSubtractionActive = false isMultiplicationActive = false case "X": isMultiplicationActive = true // 곱셈 버튼 토글 isDivisionActive = false isAdditionActive = false isSubtractionActive = false case "-": isSubtractionActive = true // 뺄셈 버튼 토글 isAdditionActive = false isMultiplicationActive = false isDivisionActive = false case "+": isAdditionActive = true // 덧셈 버튼 토글 isSubtractionActive = false isMultiplicationActive = false isDivisionActive = false default: break } ButtonAction(value) } label: { Text(value) .frame(width: value == "0" ? 160 : 80, height: 80, alignment: value == "0" ? .leading : .center) .background((value == "÷" && isDivisionActive) || (value == "X" && isMultiplicationActive) || (value == "-" && isSubtractionActive) || (value == "+" && isAdditionActive) ? Color.white : backcolor) //.background(value == "÷" && isDivisionActive ? Color.white : backcolor) //.background(value == "X" && isMultiplicationActive ? Color.white : backcolor) .foregroundColor(value == "÷" && isDivisionActive || (value == "X" && isMultiplicationActive) || (value == "-" && isSubtractionActive) || (value == "+" && isAdditionActive) ? Color.orange : fcolor) .font(.system(size: 33)) .cornerRadius(40) } } func ButtonAction(_ value: String){ switch value { // 버튼을 누르면 case "AC", "C": // C를 누르면 Reset() // 초기화 버튼을 누르면 각종 상수 및 변수들이 초기화 됨 case "÷": // 나누기를 누르면 Division() // 실질적인 계산을 하는 함수 op = "÷" ToggleCheck = false case "X": // 곱하기를 누르면 Multiplication() // 실질적인 계산을 하는 함수 op = "X" ToggleCheck = false case "-": // 빼기를 누르면 Minus() // 실질적인 계산을 하는 함수 op = "-" ToggleCheck = false case "+": // 더하기를 누르면 Plus() // 실질적인 계산을 하는 함수 op = "+" ToggleCheck = false case "+/-": printNumber = String(Int(printNumber)! * -1) case "=": Equal() // 최종 결과를 출력하기 위한 스위치문 호출 op = " " // 내가 원하는 기능을 구현하기 위해 꼭 필요한 코드 printNumber = String(totalNumber) // 연산의 결과 값을 출력 totalNumber = Int(printNumber)! default: if printNumber == "0"{ printNumber = value // 숫자 첫 입력시 화면에 출력 buttonData[0][0] = "C" } else if (isDivisionActive || isMultiplicationActive || isSubtractionActive || isAdditionActive) && ToggleCheck == false{ printNumber = value ToggleCheck = true // 연산자 버튼이 눌러짐 } else{ if printNumber.count < 9{ printNumber += value } } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }안녕하세요! 다름이 아니라 강의에 있는 프로젝트 외에 따로 계산기를 하나 만들어보고 있습니다 근데 @State 어노테이션에 대해 이해가 안되는 부분이 있습니다 구글링도 해보고 여러 방면으로 알아봤는데 원하는 답을 얻지 못해서 질문 올립니다 @State는 뷰의 상태 변화를 체크하는 어노테이션이라고 알고 있습니다. 변수 선언부의 주석을 보시면 @State 없으면 에러 왜? 이런 내용의 주석이 달려 있는 변수가 지금 저의 의문점입니다 ToggleCheck 변수와 totalNumber 변수는 뷰의 상태 변경에 직접적인 영향을 미치지 않기 때문에 @State가 필요 없다고 생각합니다. 근데 저 두 변수에 @State를 지우면 에러가 발생합니다. 일단 뷰의 변화는 실질적으로 totalNumber 변수의 값을 대입 받는 printNumber 변수이니 printNumber 변수에만 @State가 필요하다고 생각하는데 왜 에러가 발생하는 걸까요 마찬가지로 ToggleCheck 변수도 마찬가지로 체크용으로 사용하고 저 값이 변한다고 해서 직접적으로 뷰에 미치는 영향이 없는데 왜 @State가 필요한 걸까요... 혹시 알고 계시면 꼭 답변 부탁드리겠습니다...!감사합니다!
-
미해결Apple 출신 개발자의 SwiftUI Basic Part 1 기초튼튼
마지막 생성자 코드에서 enum이 안쓰이고 있는거 같아요
enum CarBrand 다 정의되고나서는마지막 생성자 코드가 init(country: String, brand: CarBrand) { self.country = country switch brand { case .Hyundai: self.title = "Hyundai" self.backgroundColor = Color.yellow case .Tesla: self.title = "Tesla" self.backgroundColor = Color.pink }}이렇게 switch 구문 패턴매칭으로 쓰는게 enum 타입을 활용해야 하지 않나요?강의에서 마지막 코드는 생성자에서 country 국가명을 if문으로 조건 분기해서 뷰를 그리고 있습니다 (brand 파라미터가 뷰를 그리는데 안쓰이고 있어요)
-
해결됨앨런 iOS 앱 개발 (15개의 앱을 만들면서 근본원리부터 배우는 UIKit) - MVVM까지
타이머 앱 강한 참조 사이클 해결 방법에 있습니다!
안녕하세요!타이머 앱 코드 설명중(11강 15:27)에서 강한 참조 사이클 부분에 궁금증이 생겨 질문 드립니다! 참조 사이클을 방지하기 위해서 weak var timer: Timer? weak self 위 2개 방식 중에서 [self]로 코드 사용 편리함을 위해 weak var timer로 택하셨다고 했는데Q: timer 변수는 의미상 직접적으로 Timer를 참조해야 맞을 것 같은데... 어떤 방법이 좋은 것인지 헷갈려서요!둘다 해결 방법인데,어떤 상황에 어떤 방법을 채택해야할지 궁금해서 질문 남겼습니다!
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
6-7) 이미지 사이즈 지정 위치 질문드립니다.
안녕하세요 애구마님!6-7) 7:50 쯤에 이미지의 사이즈를 지정해주기 위해 frame() 작성하시는 부분에 질문이 있습니다.bigBanner의 이미지 사이즈 지정 시AsyncImage(url:URL(string:bigBanner)) { image in image .resizable() .cornerRadius(10) .aspectRatio(contentMode: .fit) // 여기서 사이즈 지정 .frame(width: 300, height: 525) }poster의 이미지 사이즈 지정 시 AsyncImage(url: url) { image in image .resizable() } placeholder: { ProgressView() .tint(Color.white) } // 여기서 사이즈 지정 .frame(width: 100, height: 175) 첫 번째 AsyncImage를 작성하셨을 때 이미지 사이즈는 image에서 직접적으로 작성하셨는데 두번째 포스터들의 이미지 사이즈를 작성하실 때는 placeholder 아래쪽에 작성하신 이유가 궁금합니다.답변 부탁드립니다.감사합니다 :)
-
미해결UIKit - iOS14 실무 가이드 <iOS앱 진짜 개발자 되기>
movieapp 3 46분 실행이 안돼요ㅜㅜ
뭔가 제가 코드를 잘못 친걸수도 있는데 처음부터 보면서 비교했는데도 정말 모르겠어요ㅜㅜ...알 수 있을까요?moviemodel// // MovieModel.swift // MovieApp import Foundation struct MovieModel: Codable{ let resultCount: Int let results: [Result] } struct Result: Codable{ let trackName: String let previewUrl: String let image: String //이름 똑같이 쓸거면 쓸필요 없다 enum CodingKeys: String, CodingKey{ case image = "artworkUrl100" case trackName case previewUrl } } moviecell// // MovieCell.swift // MovieApp import UIKit class MovieCell: UITableViewCell{ @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var dateLabel: UILabel! @IBOutlet weak var descriptionLabel: UILabel! @IBOutlet weak var priceLabel: UILabel! @IBOutlet weak var movieImageView: UIImageView! } viewcontroller// // ViewController.swift // MovieApp import UIKit class ViewController: UIViewController { var movieModel: MovieModel? @IBOutlet weak var searchBar: UISearchBar! @IBOutlet weak var movieTableView: UITableView! override func viewDidLoad() { super.viewDidLoad() movieTableView.delegate = self movieTableView.dataSource = self searchBar.delegate = self requestMovieAPI() } //network 호출해서 뿌려주는 부분! //일종의 규격같은 느낌 이렇게들 씁니다 func requestMovieAPI(){ let sessionConfig = URLSessionConfiguration.default let session = URLSession(configuration: sessionConfig) // https://itunes.apple.com/search?term=marvel&media=movie var components = URLComponents(string: "https://itunes.apple.com/search") let term = URLQueryItem(name: "term", value: "marvel") let media = URLQueryItem(name: "media", value: "movie") components?.queryItems = [term, media] guard let url = components?.url else{ return } var request = URLRequest(url: url) print(request) request.httpMethod = "GET" let task = session.dataTask(with: request) { data, response, error in // 제일 중요한 부분 //statusCode //200 - 성공 //300 - 다른 주소로 넘어가서 줄수있다(리다이렉션) //400 - 에러(내가 잘못해서) //500 - 서버, 네트워크가 아예 잘못된 경우 print((response as! HTTPURLResponse).statusCode) if let hasData = data{ do{ //규격에 맞춰 써야함 ituns api는 json타입으로 줌 self.movieModel = try JSONDecoder().decode(MovieModel.self, from: hasData) print(self.movieModel ?? "no data") DispatchQueue.main.async{ //데이터 갱신 self.movieTableView.reloadData() } }catch{ print(error) } } } task.resume() session.finishTasksAndInvalidate() } } extension ViewController: UITableViewDelegate, UITableViewDataSource{ //개수 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.movieModel?.results.count ?? 0 } //MovieCell과 연결 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) as! MovieCell cell.titleLabel.text = self.movieModel?.results[indexPath.row].trackName return cell } } extension ViewController: UISearchBarDelegate{ func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { } }
-
미해결아이폰 앱 개발 기본편(iOS 개발 부트캠프)
강의 자료는 어디서 다운로드 받나요?
강의 자료를 아무리 찾아도 안 보입니다.
-
미해결계산기앱으로 시작해보는 SwiftUI
Button에 대해 이해가 되지 않는 부분이 있어서 질문드립니다
import SwiftUI struct ContentView: View { @State private var Number: Int = 0 // @State 없으면 에러 발생 왜? 추후에 알아보자 @State private var op: String = "" // 연산자 저장 변수 @State private var totalNumber: String = "0" // 실제로 계산기에 출력할 값 private let buttonData: [[String]] = [ // 키패드 모음 2차원 배열 ["C", "+/-", "%", "÷"], ["7", "8", "9", "X"], ["4", "5", "6", "-"], ["1", "2", "3", "+"], ["0", ".", "="]] var body: some View { ZStack{ Color.black.ignoresSafeArea() // 화면 전체 여백 없이 배경색 지정 VStack { Spacer() HStack{ Spacer() Text(totalNumber) // 초기값 0 출력 .padding() .font(.system(size: 73)) .foregroundColor(.white) } ButtonPosition() // C, +/-, %를 제외한 버튼을 화면에 추가 } } } func ButtonPosition() -> some View{ // 버튼 디자인 및 포지셔닝 함수 ForEach(buttonData, id: \.self){ line in HStack{ ForEach(line, id: \.self){ row in switch row{ case "C", "+/-", "%": ButtonDesign(row, .gray, .black) case "÷", "X", "-", "+", "=": ButtonDesign(row, .orange, .white) default: ButtonDesign(row, .init("NumberButton"), .white) } } } } } func ButtonDesign(_ value: String, _ backcolor: Color, _ fcolor: Color) -> some View{ // 공통 버튼 디자인 Button{ ButtonAction(value) // value는 버튼에 들어갈 글자 및 계산 값 } label: { Text(value) // 버튼 크기 및 각 버튼의 숫자 위치 지정 .frame(width: value == "0" ? 160 : 80, height: 80, alignment: value == "0" ? .leading : .center) // "0"일 때만 여백 추가, 대괄호로 값 패스 가능 //.padding(value == "0" ? .leading : []) .background(backcolor) // 버튼 배경색 .cornerRadius(40) // 버튼 모양 다듬기 .foregroundColor(fcolor) // 글자색 .font(.system(size: 33)) // 글자 사이즈 } } func ButtonAction(_ value: String){ // 내일 이 스위치문에 프로퍼티 옵저버 사용해볼 것 switch value { // value는 버튼에 들어갈 내용 및 배열에서 받아온 값 case "C": // C를 누르면 totalNumber = "0" // 계산기 초기화 case "÷" ,"X", "-", "+": Number = Int(totalNumber)! totalNumber = "0" case "=": totalNumber = String(Int(totalNumber)! + Int(Number)) default: if totalNumber == "0" { // 값이 0인 상태에서 입력이 들어오면 계산기의 첫 입력이니 totalNumber = value // 누른 버튼의 숫자가 출력됨 if Int(value) == nil{ // 첫 입력이라 숫자를 입력해야 하는데 숫자 외 다른 걸 입력받으면 totalNumber = "0" // 별다른 동작 없이 초기값 그대로 0을 유지한다 } } else{ if Int(value) != nil{ // value가 숫자형 문자열이면 totalNumber += value // 계산기에 실제로 입력한다 Number = Int(totalNumber)! } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } }제공해주신 강의를 보면서 제가 알고 있는 스위프트 문법 지식과 접목시켜서 코딩 해보고 있는 수강생입니다 제가 이해가 안되는 부분은 ButtonDesign 함수 내에서 ButtonAction 함수를 호출하면서 value를 넘겨주는 부분이 이해가 되지 않아서 글을 올립니다 계산기의 버튼을 누르면 value에 누른 버튼의 값이 들어가게 되는데 이 구조가 이해가 되지 않습니다 label은 ForEach를 통해 value에 값을 넘겨서 뷰를 띄운 게 이해가 되는데 ButtonAction 함수의 매개 변수로 넘기는 value는 ForEach를 사용하는 것도 아니고 도대체 어떻게 누른 버튼의 값을 알고 계산기에 출력을 하는 건지 모르겠습니다! 좋은 강의 만들어주셔서 감사합니다 남은 강의도 열심히 수강하겠습니다 감사합니다!
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
강의 자료를 모아보는 프로그램을 제작해보려고 합니다!
안녕하세요! 다름이 아니라 강의 자료를 모아보는 앱을 하나 만드려고 하는 상황입니다이 앱을 만들기 위해 애구마님 강의도 열심히 들을 예정입니다ㅎㅎ 예를 들어 1강부터~ 12강까지 강의가 있고 각각 pdf나 ppt 등의 강의 자료가 있다고 가정했을 때스위프트UI의 List 기능을 이용해서 리스트로 만들어 자료를 쉽게 찾아볼 수 있도록 만들고 싶습니다다만 지금 막힌 부분이 일단 List의 각각의 제목을 작성해줘야 하는데 제목을 파일명에서 긁어오고 싶습니다그리고 막힌 부분이 하나 더 있는데 파일을 불러오는 코드를 작성하고 싶은데 잘 모르겠습니다 구글링을 해보니까 PDFKIT이라는 라이브러리가 있는 것 같은데 제가 자료를 못 찾은 건지 UIKIT으로 작성된 예제 코드들 뿐이어서 파일 불러오기부터 막힌 상황입니다...! 혹시 괜찮으시다면 번거로우시겠지만 도와주실 수 있으실까요?? 구현하고자 하는 앱의 구조는 대강 아래와 같습니다일단 앱을 설치하면 아이폰이나 아이패드 내부에 해당 앱에서 볼 강의 자료 파일들을 담을 폴더가 자동으로 생성각각의 강의 자료들을 폴더로 옮김폴더에 파일이 들어올 때마다 순서대로 List에 추가(파일 이름에 1강 or 1. 등 순서를 구분할 수 있는 인덱스를 포함할 예정)실제로 뷰에서 보이는 List의 각각의 이름들은 파일 이름을 그대로 긁어오고 싶음 이 정도입니다..! 아 참고로 아주 기본적인 내용 뿐이지만 Text 뷰나 Button, (V, H, Z)Stack 등 아주 아주 기본적인 내용은 이해하고 있는 상황입니다 ㅎㅎ 아직 강의를 초반까지 밖에 듣지 않았지만 굉장히 쉽게 설명해주시려고 노력하는 부분이 저 같은 초심자에게는 굉장히 크게 와닿는 것 같습니다 남은 강의도 열공 해보겠습니다 좋은 하루 보내세요! 감사합니다!
-
해결됨UIKit - iOS14 실무 가이드 <iOS앱 진짜 개발자 되기>
missing constraints 에러
구글링을 하다 못찾겠어서 질문게시판에 남겨요!ㅠㅠsetting app_6 강의에서 20분대쯤에uiview로 선을 만들어서 위치조정을 해주는데이런 missing constraints에러가 나요!우선 저는 강의를 똑같이 따라했어요. 순서도 똑같이요!uiview를 하나 끌고와서 높이1, Email과 vertical spacing 10, tailling이랑 leading 0 으로 맞춰주었어요.그런데 height도 1로 안변하고(여전히 상자가 두껍고...) email이랑 spacing도 제대로 안된거같고...유일하게 적용된게 tailling이랑 leading 0으로 된 것이에요...ㅠㅠ 왜 이런걸까요??
-
해결됨UIKit - iOS14 실무 가이드 <iOS앱 진짜 개발자 되기>
콘솔창 print 내용 안 뜸
Dispatch Queue강의를 듣는도중에앱을 실행시키고 맨 위 버튼을 누르면 숫자가 콘솔창에 출력되어야하는데 출력되지 않습니다ㅠㅠ맨 밑에 all output이라 떠야하는데 저는 눈모양이랑 토글?모양이 나오는 점이 다른 것 같은데 어떻게 바꾸나요...
-
해결됨Part1: 진짜 왕초보 iOS 배우기(SwiftUI, SwiftData, 2024)
NavigationLink 질문
NavigationLink를 통해서 화면 전환을 하게 되면 기본적으로 > Back 버튼이 생성이 됩니다.혹시 이걸 제가 원하는 형태로 커스텀 할 수 있을까요?