ソースコード source code

下記アプリの主要なソースコードを公開しています。アプリ開発の参考になれば幸いです。

画像等が別途必要ですので下記情報のみでアプリが完成するものではありません。 アプリは少しずつ機能拡張していますのでストア公開されているアプリと内容が異なる場合があります。 コードはコピーして自由にお使いいただけます。ただし著作権は放棄しておりませんので全部の再掲載はご遠慮ください。部分的に再掲載したり、改変して再掲載するのは構いません。 自身のアプリ作成の参考として個人使用・商用問わず自由にお使いいただけます。 コード記述のお手本を示すものではありません。ミニアプリですので変数名などさほど気遣いしていない部分も有りますし間違いも有るかと思いますので参考程度にお考え下さい。 他の賢者の皆様が公開されているコードを参考にした箇所も含まれます。iOSアプリ開発の熟練者が書いたコードではありません。 エンジニア向け技術情報共有サービスではありませんので説明は省いています。ご了承ください。 GitHubなどへの公開は予定しておりません。

下記コードの最終ビルド日: 2021-04-23

ConstValue.swift

//
//  ConstValue.swift
//  TwoSidesOfCoin
//
//  Created by akira ohmachi on 2021/04/02.
//

import SwiftUI

class ConstValue: ObservableObject {
    
    //background
    static let colorBg: Color = Color.init(red:0.59,green:0.51,blue:0.33)
    
    //button color
    static let colorStart: Color = Color.init(red:0.238, green:0.77, blue:0.387)
    static let colorSetting: Color = Color.init(red:0.543, green:0.77, blue:0.289)
}

ContentView.swift

//
//  ContentView.swift
//  TwoSidesOfCoin
//
//  Created by akira ohmachi on 2021/04/02.
//

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var pub: PublicManager
    @State private var busyFlag: Bool = false
    @State private var destroyFlag: Bool = false
    @State private var coinImageName: String = "coin_a_001"
    @State private var timeLine: Int = 0
    @State private var coinChoice: Int = 0 //0 or 1
    private var coinList: Array<String> = ["a","b"]

    var body: some View {
        NavigationView {
            GeometryReader { bodyView in
                VStack(spacing: 0) {
                    HStack(spacing: 0) {
                        Button(action:{
                            self.tossCoin()
                        }){
                            Text("TossACoin")
                                .frame(width:bodyView.size.width / 4 * 3, height:100, alignment: .center)
                                .background(ConstValue.colorStart)
                                .foregroundColor(Color.white)
                        }
                        Rectangle().fill(Color.white).frame(width: 1,height: 100)
                        NavigationLink(destination: SettingView(),isActive: self.$pub.isSetting) {
                            Button(action:{
                                if (self.busyFlag) {
                                    return
                                }
                                self.pub.tmpSideAratio = self.pub.sideAratio
                                self.pub.tmpSideBratio = self.pub.sideBratio
                                self.pub.isSetting = true
                            }){
                                Text("Setting")
                                    .frame(width:bodyView.size.width / 4, height:100, alignment: .center)
                                    .background(ConstValue.colorSetting)
                                    .foregroundColor(Color.white)
                            }
                        }
                    }
                    .frame(width:bodyView.size.width, height:100)
                    Rectangle().fill(Color.white).frame(width: bodyView.size.width,height: 1)
                    ZStack(alignment: .bottom) {
                        ScrollView {
                            Image(self.coinImageName)
                                .resizable()
                                .frame(width:bodyView.size.width, height:bodyView.size.width, alignment: .center)
                            Spacer(minLength: 150)
                        }
                        HStack(spacing: 0) {
                            Spacer(minLength: 0)
                            PublicManager.AdView().frame(maxWidth: bodyView.size.width, maxHeight: 50)
                            Spacer(minLength: 0)
                        }.background(Color(red:0.2,green:0.2,blue:0.2))
                    }
                }
                .background(ConstValue.colorBg)
                //.navigationBarTitle("")
                .navigationBarHidden(true)
            }
        }
        .navigationViewStyle(StackNavigationViewStyle())
        .onDisappear {     //アプリ終了時
            self.destroyFlag = true
        }
    }

    //Toss a coin button on click
    func tossCoin() {
        if self.busyFlag {
            return
        }
        self.busyFlag = true
        //set a coinChoice
        let ratioA: Int = NumberFormatter().number(from: "0" + pub.sideAratio) as! Int
        let ratioB: Int = NumberFormatter().number(from: "0" + pub.sideBratio) as! Int
        let sumRatio: Int = ratioA + ratioB
        self.coinChoice = 0
        if Int.random(in: 0..<sumRatio) >= ratioA {
            self.coinChoice = 1
        }
        //
        self.timeLine = 0
        self.timeLineAnimation()
    }

    //coin animation
    func timeLineAnimation() {
        withAnimation {
            if self.destroyFlag {
                return
            }
            self.timeLine += 1
            if self.timeLine > 120 {
                self.busyFlag = false
                return
            }
            self.setImageName()
            DispatchQueue.global().async {
                Thread.sleep(forTimeInterval: 0.02)
                DispatchQueue.main.sync {
                    if self.destroyFlag == false {
                        self.timeLineAnimation()
                    }
                }
            }
        }
    }

    //set coin image
    func setImageName() {
        self.coinImageName = "coin_" + self.coinList[self.coinChoice] + "_" + String(format: "%03d",self.timeLine)
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(PublicManager())
    }
}

PublicManager.swift

//
//  PublicManager.swift
//  TwoSidesOfCoin
//
//  Created by akira ohmachi on 2021/04/02.
//

import SwiftUI
import GoogleMobileAds

class PublicManager: ObservableObject {
    
    @AppStorage("sideAratio") var sideAratio: String = "1"
    @AppStorage("sideBratio") var sideBratio: String = "1"
    @Published var isSetting: Bool = false
    
    @Published var tmpSideAratio: String = ""
    @Published var tmpSideBratio: String = ""

    //--------------------------------------------------
    //adMob
    
    struct AdView: UIViewRepresentable {
        func makeUIView(context: Context) -> GADBannerView {
            let banner = GADBannerView(adSize: kGADAdSizeBanner)
            #if DEBUG
            banner.adUnitID = "ca-app-pub-3940256099942544/2934735716"
            #else
            banner.adUnitID = "ca-app-pub-0000000000000000/0000000000"
            #endif
            banner.rootViewController = UIApplication.shared.windows.first?.rootViewController
            banner.load(GADRequest())
            return banner
        }

        func updateUIView(_ uiView: GADBannerView, context: Context) {
        }
    }

    //--------------------------------------------------

}

SettingView.swift

//
//  SettingView.swift
//  TwoSidesOfCoin
//
//  Created by akira ohmachi on 2021/04/02.
//

import SwiftUI

struct SettingView: View {
    @EnvironmentObject var pub: PublicManager
    
    var body: some View {
        VStack() {
            HStack {
                Button(action: {
                    pub.isSetting = false
                }) {
                    HStack {
                        Image(systemName: "arrow.backward")
                        Text("Cancel")
                    }
                }
                Spacer()
                Button(action: {
                    self.pub.sideAratio = self.adjustStrNum(str: self.pub.tmpSideAratio)
                    self.pub.sideBratio = self.adjustStrNum(str: self.pub.tmpSideBratio)
                    pub.isSetting = false
                }) {
                    HStack {
                        Text("Apply")
                        Image(systemName: "checkmark.circle")
                    }
                }
            }
            .padding(20)
        }
        Form {
            Section(header: Text("RatioOfSideA")) {
                TextField("", text: Binding(
                    get: {pub.tmpSideAratio},
                    set: {pub.tmpSideAratio = $0.filter{"0123456789".contains($0)}}))
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .keyboardType(.numberPad)
            }
            Section(header: Text("RatioOfSideB")) {
                TextField("", text: Binding(
                    get: {pub.tmpSideBratio},
                    set: {pub.tmpSideBratio = $0.filter{"0123456789".contains($0)}}))
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .keyboardType(.numberPad)
            }
            Text("Usage")
                .font(.footnote)
        }
        //.navigationBarTitle("Setting",displayMode: .automatic)
        //.navigationViewStyle(StackNavigationViewStyle())
        .navigationBarHidden(true)
        .onTapGesture {     //背景タップでキーボードを閉じる
            UIApplication.shared.closeKeyboard()
        }

    }
    
    func adjustStrNum(str: String) -> String {
        var num: Int = NumberFormatter().number(from: "0" + str) as! Int
        if num == 0 {
            num = 1
        }
        return String(num)
    }
}

extension UIApplication {
    func closeKeyboard() {  //キーボードを閉じる
        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

struct SettingView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            SettingView().environmentObject(PublicManager())
        }
    }
}

TwoSidesOfCoinApp.swift

//
//  TwoSidesOfCoinApp.swift
//  TwoSidesOfCoin
//
//  Created by akira ohmachi on 2021/04/02.
//

import SwiftUI
import UIKit
import GoogleMobileAds

// AppDelegateクラスを定義する
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {

        // Mobile Ads SDKを初期化する
        GADMobileAds.sharedInstance().start(completionHandler: nil)
        
        return true
    }
}

@main
struct TwoSidesOfCoinApp: App {
    // SwiftUI AppライフサイクルにAppDelegateクラスを注入する
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(PublicManager())
        }
    }
}