如何在 TS 上从 Iphone 访问加速度计?
41
0
在为 Telegram 开发 Web 应用程序时,我遇到了一个相当有趣的案例。最重要的是,我需要获得手机用户(特别是 iPhone 用户)的许可(或拒绝)......
在为 Telegram 开发 Web 应用程序时,我遇到了一个相当有趣的案例。
最重要的是,我需要在第一个应用程序上获得手机用户(特别是 iPhone 用户)的许可(或拒绝),并且我可以使用手机的加速度计来识别某些事件。
本地 - 它可以工作(如果有人感兴趣,可以使用 zrok 作为层,因为 Telegram 不会在没有证书的情况下打开本地版本)
但是,部署后,iPhone 上的工作没有任何停止。Android 上的情况要好得多)
以下是我为了明确获得 iPhone 所有者的许可而编写的代码部分......
async approve(): Promise<boolean> {
if (typeof this._approved === 'undefined') {
if (!('DeviceMotionEvent' in window)) return (this._approved = false)
try {
type PermissionRequestFn = () => Promise<PermissionState>
type DME = typeof DeviceMotionEvent & { requestPermission: PermissionRequestFn }
if (typeof (DeviceMotionEvent as DME).requestPermission === 'function') {
const permissionState = await (DeviceMotionEvent as DME).requestPermission()
this._approved = permissionState === 'granted'
} else this._approved = true
} catch {
this._approved = false
}
}
return this._approved
}
export const Home = observer(() => {
const { x, y, z } = useShake()
const shaker = new Shake({ threshold: 5, timeout: 50 })
const handleApproval = (evt: React.MouseEvent<HTMLButtonElement>) => {
evt.preventDefault()
shaker.approve()
}
const { handleSuccessHaptic } = useHaptic()
useEffect(() => {
shaker.addEventListener('shake', () => {
game.onClick()
handleSuccessHaptic()
})
shaker.start()
return () => {
shaker.stop()
}
}, [handleSuccessHaptic, shaker])
const handleClicks = () => {
console.log('It work')
}
return (
<div className={'flex flex-col gap-10'}>
<Button className={'w-full'} onClick={handleApproval}>
GET APPROVE
</Button>
<ul>
<li>X: {x}</li>
<li>Y: {y}</li>
<li>Z: {z}</li>
</ul>
</div>
)
})
如何在 TS 上从 Iphone 访问加速度计?
下载声明: 本站所有软件和资料均为软件作者提供或网友推荐发布而来,仅供学习和研究使用,不得用于任何商业用途。如本站不慎侵犯你的版权请联系我,我将及时处理,并撤下相关内容!
下载声明: 本站所有软件和资料均为软件作者提供或网友推荐发布而来,仅供学习和研究使用,不得用于任何商业用途。如本站不慎侵犯你的版权请联系我,我将及时处理,并撤下相关内容!
收藏的用户(0)
X
正在加载信息~
-
我正在运行 xcode v. 15.3、Swift v. 5.10,并在 iPhone iOS v. 17.4.1 上进行测试。在尝试实现简单的共享表功能时,我不断遇到一个奇怪的错误,这将允许......
我正在运行 xcode v. 15.3、Swift v. 5.10,在 iPhone iOS v. 17.4.1 上进行测试
我在尝试实现简单的共享表功能(允许用户共享存储的图像)时一直遇到一个奇怪的错误。我花了两天时间尝试在线寻找解决方案,但没有成功。
起初我以为是存储问题,因此我尝试了 SwiftData、CoreData 和文件系统存储,结果相同。
单击带有图像的待办事项,然后单击底部的共享图标。应该显示共享表,但什么也没有显示,我在日志中看到以下错误
Couldn't read values in CFPrefsPlistSource<0x302f410e0> (Domain: com.apple.country.carrier_2, User: kCFPreferencesCurrentUser, ByHost: No, Container: /var/mobile/Library/CountryBundles/, Contents Need Refresh: Yes): accessing preferences outside an application's container requires user-preference-read or file-read-data sandbox access Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.primitiveattribute AND originator doesn't have entitlement com.apple.runningboard.assertions.frontboard AND target is not running or doesn't have entitlement com.apple.runningboard.trustedtarget AND Target not hosted by originator)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.primitiveattribute AND originator doesn't have entitlement com.apple.runningboard.assertions.frontboard AND target is not running or doesn't have entitlement com.apple.runningboard.trustedtarget AND Target not hosted by originator)}> (501) personaAttributesForPersonaType for type:0 failed with error Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.mobile.usermanagerd.xpc was invalidated: failed at lookup with error 159 - Sandbox restriction." UserInfo={NSDebugDescription=The connection to service named com.apple.mobile.usermanagerd.xpc was invalidated: failed at lookup with error 159 - Sandbox restriction.} Received port for identifier response: <(null)> with error:Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.process-state, NSLocalizedFailureReason=Client not entitled, RBSPermanent=false} elapsedCPUTimeForFrontBoard couldn't generate a task port Received port for identifier response: <(null)> with error:Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.process-state, NSLocalizedFailureReason=Client not entitled, RBSPermanent=false} Attempt to present <UIActivityViewController: 0x10582e800> on <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x10301e800> (from <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x10301e800>) which is already presenting <_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView_: 0x10700ee00>. connection invalidated
完整应用程序代码:
// // ToDosApp.swift // ToDos // // Created by Tunde Adegoroye on 06/06/2023. // import SwiftUI import SwiftData @main struct ToDosApp: App { @AppStorage("isFirstTimeLaunch") private var isFirstTimeLaunch: Bool = true var body: some Scene { WindowGroup { ContentView() } .modelContainer(ItemsContainer.create(shouldCreateDefaults: &isFirstTimeLaunch)) } }
// // ContentView.swift // ToDos // // Created by Tunde Adegoroye on 06/06/2023. // import SwiftUI import SwiftData import SwiftUIImageViewer enum SortOption: String, CaseIterable { case title case date case category } extension SortOption { var systemImage: String { switch self { case .title: "textformat.size.larger" case .date: "calendar" case .category: "folder" } } } struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var items: [Item] @State private var searchQuery = "" @State private var showCreateCategory = false @State private var showCreateToDo = false @State private var toDoToEdit: Item? @State private var isImageViewerPresented = false @State private var selectedSortOption = SortOption.allCases.first! var filteredItems: [Item] { if searchQuery.isEmpty { return items.sort(on: selectedSortOption) } let filteredItems = items.compactMap { item in let titleContainsQuery = item.title.range(of: searchQuery, options: .caseInsensitive) != nil let categoryTitleContainsQuery = item.category?.title.range(of: searchQuery, options: .caseInsensitive) != nil return (titleContainsQuery || categoryTitleContainsQuery) ? item : nil } return filteredItems.sort(on: selectedSortOption) } var body: some View { NavigationStack { List { ForEach(filteredItems) { item in VStack { HStack { VStack(alignment: .leading) { if item.isCritical { Image(systemName: "exclamationmark.3") .symbolVariant(.fill) .foregroundColor(.red) .font(.largeTitle) .bold() } Text(item.title) .font(.largeTitle) .bold() Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .shortened))") .font(.callout) if let category = item.category { Text(category.title) .foregroundStyle(Color.blue) .bold() .padding(.horizontal) .padding(.vertical, 8) .background(Color.blue.opacity(0.1), in: RoundedRectangle(cornerRadius: 8, style: .continuous)) } } Spacer() Button { withAnimation { item.isCompleted.toggle() } } label: { Image(systemName: "checkmark") .symbolVariant(.circle.fill) .foregroundStyle(item.isCompleted ? .green : .gray) .font(.largeTitle) } .buttonStyle(.plain) } if let selectedPhotoData = item.image, let uiImage = UIImage(data: selectedPhotoData) { Image(uiImage: uiImage) .resizable() .scaledToFill() .frame(maxWidth: .infinity, maxHeight: 120) .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) .onTapGesture { isImageViewerPresented = true } .fullScreenCover(isPresented: $isImageViewerPresented) { SwiftUIImageViewer(image: Image(uiImage: uiImage)) .overlay(alignment: .topTrailing) { Button { isImageViewerPresented = false } label: { Image(systemName: "xmark") .font(.headline) } .buttonStyle(.bordered) .clipShape(Circle()) .tint(.purple) .padding() } Button(action: { shareImage(uiImage) }) { Label("Share", systemImage: "square.and.arrow.up") .foregroundColor(.blue) } .buttonStyle(.plain) } } } .swipeActions { Button(role: .destructive) { withAnimation { modelContext.delete(item) } } label: { Label("Delete", systemImage: "trash.fill") } Button { toDoToEdit = item } label: { Label("Edit", systemImage: "pencil") } .tint(.orange) } } } .navigationTitle("My To Do List") .animation(/*@START_MENU_TOKEN@*/.easeIn/*@END_MENU_TOKEN@*/, value: filteredItems) .searchable(text: $searchQuery, prompt: "Search for a to do or a category") .overlay { if filteredItems.isEmpty { ContentUnavailableView.search } } .sheet(item: $toDoToEdit, onDismiss: { toDoToEdit = nil }, content: { editItem in NavigationStack { UpdateToDoView(item: editItem) .interactiveDismissDisabled() } }) .sheet(isPresented: $showCreateCategory, content: { NavigationStack { CreateCategoryView() } }) .sheet(isPresented: $showCreateToDo, content: { NavigationStack { CreateTodoView() } }) .toolbar { ToolbarItemGroup(placement: .primaryAction) { Button { showCreateCategory.toggle() } label: { Image(systemName: "plus") } } ToolbarItemGroup(placement: .topBarTrailing) { Menu { Picker("", selection: $selectedSortOption) { ForEach(SortOption.allCases, id: \.rawValue) { option in Label(option.rawValue.capitalized, systemImage: option.systemImage) .tag(option) } } .labelsHidden() } label: { Image(systemName: "ellipsis") .symbolVariant(.circle) } } } .safeAreaInset(edge: .bottom, alignment: .leading) { Button(action: { showCreateToDo.toggle() }, label: { Label("New ToDo", systemImage: "plus") .bold() .font(.title2) .padding(8) .background(.gray.opacity(0.1), in: Capsule()) .padding(.leading) .symbolVariant(.circle.fill) }) } } } private func shareImage(_ image: UIImage) { let activityController = UIActivityViewController(activityItems: [image], applicationActivities: nil) // Selecting the appropriate scene let targetScene = UIApplication.shared.connectedScenes .filter { $0.activationState == .foregroundActive } .first(where: { $0 is UIWindowScene }) as? UIWindowScene guard let rootViewController = targetScene?.windows .first(where: { $0.isKeyWindow })? .rootViewController else { return } // Present the activity controller rootViewController.present(activityController, animated: true, completion: nil) } private func delete(item: Item) { withAnimation { modelContext.delete(item) } } } private extension [Item] { func sort(on option: SortOption) -> [Item] { switch option { case .title: self.sorted(by: { $0.title < $1.title }) case .date: self.sorted(by: { $0.timestamp < $1.timestamp }) case .category: self.sorted(by: { guard let firstItemTitle = $0.category?.title, let secondItemTitle = $1.category?.title else { return false } return firstItemTitle < secondItemTitle }) } } }
// // TodoView.swift // ToDos // // Created by Tunde Adegoroye on 06/06/2023. // import SwiftUI struct TodoView: View { let item: Item @State private var isShareSheetPresented = false var body: some View { VStack(alignment: .leading) { if item.isCritical { Image(systemName: "exclamationmark.3") .symbolVariant(.fill) .foregroundColor(.red) .font(.largeTitle) .bold() } Text(item.title) .font(.largeTitle) .bold() Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") .font(.callout) } } }
// // Item.swift // ToDos // // Created by Tunde Adegoroye on 06/06/2023. // import Foundation import SwiftData @Model final class Item { var title: String var timestamp: Date var isCritical: Bool var isCompleted: Bool @Relationship(deleteRule: .nullify, inverse: \Category.items) var category: Category? @Attribute(.externalStorage) var image: Data? init(title: String = "", timestamp: Date = .now, isCritical: Bool = false, isCompleted: Bool = false) { self.title = title self.timestamp = timestamp self.isCritical = isCritical self.isCompleted = isCompleted } } extension Item { static var dummy: Item { .init(title: "Item 1", timestamp: .now, isCritical: true) } }