はじめに
こんにちは、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コードで友達とのゲーム体験をよりスムーズに!
またフレンドを募集する際にもご利用いただけます!!
【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で設定画面をモーダル表示。
まとめ
IDNect 1.5.0のプロフィールカード機能は、SwiftUIでモダンなUIとアニメーションを実装しました。
ツヤアニメーションやQRコード生成、カスタマイズ機能を追加し、ユーザー体験を向上させています。
ぜひ試してみてください!

