아이폰 근접센서 주머니 감시, 학생 목록 타일 UI, 학교 서버 주소 반영
- iOS는 조도 센서/백그라운드 서비스가 불가능해서 근접센서(UIDevice proximityMonitoring) + 가이드 접근 안내로 대체 구현 (Core NFC와 마찬가지로 네이티브 플러그인 필요해서 ProximityMonitorPlugin.swift 추가) - 교사 대시보드 실시간 출석 현황: 모바일 리스트/데스크톱 표를 타일 그리드로 통일해서 한눈에 보기 쉽게 변경 - 서버를 학교 자체 서버(Cloudflare Tunnel, api.backsanhi.shop)로 이전하면서 기본 BASE_URL 갱신 - Firebase CLI 로컬 캐시(.firebase/)는 소스가 아니라 gitignore 처리 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
5FA389C5018E5592CC402E5C /* ProximityMonitorPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = E653BC1DC10E8731479786F7 /* ProximityMonitorPlugin.swift */; };
|
||||
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
@@ -49,6 +50,7 @@
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
E653BC1DC10E8731479786F7 /* ProximityMonitorPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProximityMonitorPlugin.swift; sourceTree = "<group>"; };
|
||||
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
@@ -122,6 +124,7 @@
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
E653BC1DC10E8731479786F7 /* ProximityMonitorPlugin.swift */,
|
||||
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
);
|
||||
@@ -282,6 +285,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
5FA389C5018E5592CC402E5C /* ProximityMonitorPlugin.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */,
|
||||
);
|
||||
|
||||
@@ -12,5 +12,8 @@ import UIKit
|
||||
|
||||
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
|
||||
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
|
||||
ProximityMonitorPlugin.register(
|
||||
with: engineBridge.pluginRegistry.registrar(forPlugin: "ProximityMonitorPlugin")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// 🛡️ iOS 근접센서로 "주머니 감시"를 흉내내기 위한 네이티브 플러그인.
|
||||
// UIDevice의 근접센서를 켜면 통화 중처럼 화면이 자동으로 꺼지고 켜지는데,
|
||||
// 그 상태 변화(가까움/멀어짐)를 Dart 쪽 EventChannel로 그대로 흘려보낸다.
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
class ProximityMonitorPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
|
||||
private var eventSink: FlutterEventSink?
|
||||
|
||||
static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let instance = ProximityMonitorPlugin()
|
||||
|
||||
let methodChannel = FlutterMethodChannel(
|
||||
name: "proximity_monitor",
|
||||
binaryMessenger: registrar.messenger()
|
||||
)
|
||||
methodChannel.setMethodCallHandler(instance.handle)
|
||||
|
||||
let eventChannel = FlutterEventChannel(
|
||||
name: "proximity_monitor/events",
|
||||
binaryMessenger: registrar.messenger()
|
||||
)
|
||||
eventChannel.setStreamHandler(instance)
|
||||
}
|
||||
|
||||
func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "start":
|
||||
UIDevice.current.isProximityMonitoringEnabled = true
|
||||
result(nil)
|
||||
case "stop":
|
||||
UIDevice.current.isProximityMonitoringEnabled = false
|
||||
result(nil)
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
||||
eventSink = events
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(proximityStateChanged),
|
||||
name: UIDevice.proximityStateDidChangeNotification,
|
||||
object: nil
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||
NotificationCenter.default.removeObserver(
|
||||
self,
|
||||
name: UIDevice.proximityStateDidChangeNotification,
|
||||
object: nil
|
||||
)
|
||||
eventSink = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc private func proximityStateChanged() {
|
||||
eventSink?(UIDevice.current.proximityState)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user