0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IDNect バージョン1.5.0:プロフィールカード機能を追加!SwiftUIで実装したアニメーションとQRコード

Posted at

はじめに

こんにちは、Kosukeです!
IDNectは、ゲームIDを一元管理できる無料アプリです。iOSとAndroidアプリのバージョン1.5.0で新たにプロフィールカード機能を追加しました!

この機能では、ゲームIDをQRコードやカードで簡単に共有でき、カードの色やアニメーションをカスタマイズできます。
今回は、プロフィールカードのSwiftUI実装と、ツヤ(シャマー)アニメーション、QRコード生成のコードを解説します。

IDNectとは?

DNectは、ゲームIDを簡単に登録・管理できる無料アプリです。以下の人気のゲームも含めて多くのPC・スマホゲーム、Nintendo Switch、Xbox、Play Station、SteamのID登録に対応しています。IDNectで、複数のゲームIDを一元管理し、ID検索・QRコードで友達とのゲーム体験をよりスムーズに!
またフレンドを募集する際にもご利用いただけます!!

image.png

【Web】https://idnect.com
【iOS】 https://x.gd/dZXnZ
【Android】 https://x.gd/zMIGb

新機能:バージョン1.5.0で追加されたプロフィールカードの特徴:QRコード:自分のIDをQRコードで共有。
カスタマイズ:カードの背景色や文字色を変更可能。
ツヤアニメーション:光沢効果でカードをスタイリッシュに。
表と裏のデザイン:表にQRコード、裏にゲームIDを表示。
スクリーンショット:カードを画像として保存・共有可能。

コード解説1. プロフィールカードの全体構造(ProfileCardView)

プロフィールカードは、SwiftUIのProfileCardViewで実装。表と裏を3D回転アニメーションで切り替えます。


import SwiftUI

struct ProfileCardView: View {
    @State private var isFlipped = false // カードの表裏状態
    @State private var rotationDegrees = 0.0 // 回転角度
    @State private var cardColor: Color = .yellow // カードの背景色
    @State private var textColor: Color = .black // 文字色

    var body: some View {
        ZStack {
            // カードの背景(ツヤアニメーション付き)
            cardBackground
            // 表または裏を表示
            ZStack {
                if abs(rotationDegrees.truncatingRemainder(dividingBy: 360)) < 90 || abs(rotationDegrees.truncatingRemainder(dividingBy: 360)) > 270 {
                    frontView // 表
                } else {
                    backView // 裏(反転表示)
                        .scaleEffect(x: -1, y: 1)
                }
            }
            .clipped()
        }
        .frame(width: 350, height: 220)
        .rotation3DEffect(
            .degrees(rotationDegrees),
            axis: (x: 0, y: 1, z: 0), // Y軸で回転
            anchor: .center,
            perspective: 0.3
        )
        .onTapGesture {
            withAnimation(.easeInOut(duration: 0.6)) {
                rotationDegrees += 180 // タップで180度回転
                isFlipped.toggle()
            }
        }
    }
}

構造:ZStackで背景と表/裏を重ね、.rotation3DEffectで3D回転を実現。
状態管理:isFlippedで表裏を管理、rotationDegreesでアニメーションの角度を制御。
タップ処理:タップでカードを0.6秒で回転。

2. カードの表(frontView)カードの表は、ユーザー名とQRコードを表示します。


struct ProfileCardView: View {
    // ... 他のコード ...

    func frontView() -> some View {
        ZStack(alignment: .topLeading) {
            VStack(spacing: 12) {
                // QRコード
                Image(uiImage: generateQRCode(from: "IDNect://user?id=USER123"))
                    .resizable()
                    .interpolation(.none)
                    .scaledToFit()
                    .frame(width: 120, height: 120)
                    .background(Color.white)
                    .clipShape(RoundedRectangle(cornerRadius: 8))
                    .padding(.top, 15)
                
                // ユーザー名
                Text("@UserName")
                    .font(.system(size: 14, weight: .medium, design: .rounded))
                    .foregroundColor(textColor)
                    .padding(.bottom, 15)
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            
            // QR拡大ボタン
            Button(action: { /* QR拡大処理 */ }) {
                Image(systemName: "viewfinder")
                    .font(.system(size: 22, weight: .medium))
                    .foregroundColor(textColor.opacity(0.8))
                    .padding()
            }
        }
    }
}

QRコード:generateQRCode関数で生成したQRコードを表示。
ユーザー名:カスタマイズ可能なtextColorで表示。
拡大ボタン:QRコードをタップで拡大(実装は省略)。

3. カードの裏(backView)カードの裏は、ユーザー情報、ゲームリスト、プロフィールURL、QRコードを表示します。ゲームアイコンはSF Symbols(例: gamecontroller.fill)を使用。


struct ProfileCardView: View {
    // ... 他のコード ...

    var backView: some View {
        ZStack(alignment: .bottomLeading) {
            HStack(spacing: 15) {
                VStack(alignment: .leading, spacing: 8) {
                    // ユーザー情報
                    Text("UserName")
                        .font(.system(size: 14, weight: .medium))
                    Text("@UserID")
                        .font(.system(size: 12, weight: .regular))
                    Spacer()
                }
                .foregroundColor(textColor)
                
                Spacer()
                
                // ゲームリスト
                gameListView
            }
            .padding(12)
            
            // プロフィールURL
            HStack {
                Button(action: {
                    if let url = URL(string: "https://IDNect.com/Profile/@UserID") {
                        UIApplication.shared.open(url)
                    }
                }) {
                    Text("https://IDNect.com/Profile/@UserID")
                        .font(.system(size: 10, weight: .regular))
                        .foregroundColor(textColor)
                        .underline()
                        .lineLimit(1)
                }
                Spacer()
            }
            .padding(.horizontal, 12)
            .padding(.bottom, 8)
            
            // 小さいQRコード
            HStack {
                Spacer()
                VStack {
                    Spacer()
                    Image(uiImage: generateQRCode(from: "IDNect://user?id=USER123"))
                        .resizable()
                        .interpolation(.none)
                        .scaledToFit()
                        .frame(width: 45, height: 45)
                        .background(Color.white)
                        .clipShape(RoundedRectangle(cornerRadius: 5))
                        .padding(10)
                }
            }
        }
    }

    var gameListView: some View {
        VStack(alignment: .leading, spacing: 6) {
            // ゲームリスト(例: 2つのゲーム)
            ForEach([
                Game(id: "1", name: "Game A"),
                Game(id: "2", name: "Game B")
            ]) { game in
                HStack(spacing: 10) {
                    // ゲームアイコン(SF Symbols)
                    Image(systemName: "gamecontroller.fill")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 30, height: 30)
                        .foregroundColor(textColor)
                        .clipShape(RoundedRectangle(cornerRadius: 6))
                    
                    // ゲーム名
                    Text(game.name)
                        .font(.system(size: 12, weight: .medium))
                        .lineLimit(1)
                    Spacer()
                }
            }
        }
        .foregroundColor(textColor)
        .frame(width: 200, height: 120)
    }
}

struct Game: Identifiable {
    let id: String
    let name: String
}

ユーザー情報:ユーザー名とIDを左側に表示。
ゲームリスト:gameListViewでゲーム名とアイコン(gamecontroller.fill)を表示。実際のアプリではAPIから取得したデータを使用。
プロフィールURL:タップでブラウザを開くリンクを表示。
QRコード:右下に小さく表示し、表と同じQRコードを再利用。

4. ツヤアニメーション(cardBackground)ツヤ(シャマー)

アニメーションは、グラデーションを動かして光沢効果を表現します。


struct ProfileCardView: View {
    @State private var shimmerProgress: CGFloat = -1.5 // アニメーションの進行度
    @State private var isAnimationEnabled: Bool = true // アニメーションのオン/オフ

    // ... 他のコード ...

    var cardBackground: some View {
        RoundedRectangle(cornerRadius: 25)
            .fill(
                LinearGradient(
                    gradient: Gradient(colors: [cardColor.opacity(0.9), cardColor.opacity(0.7), cardColor.opacity(0.9)]),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
            )
            .overlay(
                // ツヤ効果のグラデーション
                RoundedRectangle(cornerRadius: 25)
                    .fill(
                        LinearGradient(
                            gradient: Gradient(colors: [.clear, Color.white.opacity(0.5), .clear]),
                            startPoint: UnitPoint(x: shimmerProgress, y: 0),
                            endPoint: UnitPoint(x: shimmerProgress + 0.5, y: 1)
                        )
                    )
                    .mask(RoundedRectangle(cornerRadius: 25))
                    .onAppear {
                        startShimmerAnimation()
                    }
            )
            .overlay(
                RoundedRectangle(cornerRadius: 25)
                    .stroke(
                        LinearGradient(
                            gradient: Gradient(colors: [.white.opacity(0.6), .white.opacity(0.2)]),
                            startPoint: .topLeading,
                            endPoint: .bottomTrailing
                        ),
                        lineWidth: 1
                    )
            )
            .shadow(color: .black.opacity(0.3), radius: 20, x: 0, y: 10)
    }

    private func startShimmerAnimation() {
        guard isAnimationEnabled else { return }
        withAnimation(.linear(duration: 5.0).repeatForever(autoreverses: false)) {
            shimmerProgress = 1.5 // アニメーションを-1.5から1.5まで移動
        }
    }
}

ツヤ効果:LinearGradientをshimmerProgressで動かし、光が流れるようなアニメーションを表現。
アニメーション制御:isAnimationEnabledでオン/オフを切り替え、.onAppearでアニメーション開始。
背景:cardColorを使ったグラデーション背景に、枠線と影を追加。

5. QRコード生成(generateQRCode)QRコードはCoreImageを使って生成します。


import CoreImage.CIFilterBuiltins

struct ProfileCardView: View {
    let context = CIContext()
    let filter = CIFilter.qrCodeGenerator()

    func generateQRCode(from string: String) -> UIImage {
        // QRコード生成
        filter.message = Data(string.utf8)
        if let outputImage = filter.outputImage {
            // スケールアップで鮮明に
            let transform = CGAffineTransform(scaleX: 10, y: 10)
            let scaledImage = outputImage.transformed(by: transform)
            if let cgImage = context.createCGImage(scaledImage, from: scaledImage.extent) {
                return UIImage(cgImage: cgImage)
            }
        }
        return UIImage(systemName: "xmark.circle") ?? UIImage()
    }
}

生成:CIFilter.qrCodeGeneratorに文字列を渡してQRコードを生成。
スケーリング:transformで10倍に拡大し、読み取りやすさを確保。

6. カスタマイズ設定(settingsView)カードの色やアニメーションをカスタマイズ可能。


struct ProfileCardView: View {
    // ... 他のコード ...

    @State private var showingSettings = false

    var body: some View {
        NavigationStack {
            // ... 他のコード ...
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: { showingSettings = true }) {
                        Image(systemName: "gearshape.fill")
                            .foregroundStyle(.black)
                    }
                }
            }
            .sheet(isPresented: $showingSettings) {
                settingsView
                    .presentationDetents([.height(250)])
            }
        }
    }

    var settingsView: some View {
        VStack(spacing: 20) {
            Text("カスタマイズ")
                .font(.headline)
            
            ColorPicker("カードの色", selection: $cardColor, supportsOpacity: false)
            ColorPicker("文字の色", selection: $textColor, supportsOpacity: false)
            Toggle("ツヤアニメーション", isOn: $isAnimationEnabled)
            
            Spacer()
        }
        .padding(30)
    }
}

カスタマイズ:ColorPickerで色を選択、Toggleでアニメーションをオン/オフ。
表示:sheetで設定画面をモーダル表示。

image.png

まとめ

IDNect 1.5.0のプロフィールカード機能は、SwiftUIでモダンなUIとアニメーションを実装しました。
ツヤアニメーションやQRコード生成、カスタマイズ機能を追加し、ユーザー体験を向上させています。
ぜひ試してみてください!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?