هذا العام ، هناك العديد من الفرص المثيرة للاهتمام لمطوري iOS
لنبدأ بالتعريف - الأدوات الذكية هي طرق عرض تعرض المعلومات ذات الصلة دون تشغيل تطبيق الهاتف المحمول الرئيسي وتكون دائمًا في متناول المستخدم. القدرة على استخدامها موجودة بالفعل في iOS ( ملحق اليوم ) ، بدءًا من iOS 8 ، لكن تجربتي الشخصية البحتة في استخدامها محزنة للغاية - على الرغم من تخصيص سطح مكتب خاص مع عناصر واجهة مستخدم لها ، ما زلت نادرًا ما أصل إلى هناك ، لم تتطور العادة.
نتيجة لذلك ، في نظام iOS 14 ، نرى عودة ظهور عناصر واجهة المستخدم ، وأكثر اندماجًا في النظام البيئي ، وأكثر سهولة في الاستخدام (من الناحية النظرية).
يعد العمل باستخدام بطاقات الولاء إحدى الوظائف الرئيسية لتطبيق Wallet الخاص بنا. من وقت لآخر ، تظهر اقتراحات المستخدمين حول إمكانية إضافة عنصر واجهة مستخدم إلى Today في المراجعات في App Store. يرغب المستخدمون ، عند الخروج ، في إظهار البطاقة في أسرع وقت ممكن ، والحصول على خصم والهرب من أعمالهم ، لأن التأخير في أي شريحة زمنية يتسبب في ظهور تلك النظرات المؤلمة في قائمة الانتظار. في حالتنا ، يمكن للأداة الذكية حفظ العديد من إجراءات المستخدم لفتح بطاقة ، مما يجعل الدفع مقابل البضائع عند الخروج أسرع. ستكون المتاجر ممتنة أيضًا - عدد أقل من قوائم الانتظار عند الخروج.
هذا العام ، أصدرت Apple بشكل غير متوقع إصدار iOS فورًا تقريبًا بعد العرض التقديمي ، مما يترك للمطورين يومًا لإنهاء تطبيقاتهم على Xcode GM ، لكننا كنا مستعدين للإصدار ، منذ أن بدأ فريق iOS لدينا في صنع نسختهم الخاصة من الأداة على الإصدارات التجريبية من Xcode ... الأداة قيد المراجعة حاليًا في متجر التطبيقات. وفقًا للإحصاءات ، فإن تحديث الأجهزة إلى نظام iOS الجديد سريع جدًا ؛ على الأرجح ، سيذهب المستخدمون للتحقق من التطبيقات التي تحتوي بالفعل على عناصر واجهة مستخدم ، والعثور على تطبيقاتنا ، وسيكونون سعداء.
في المستقبل ، نود إضافة المزيد من المعلومات ذات الصلة - على سبيل المثال ، الرصيد والرمز الشريطي وآخر الرسائل غير المقروءة من الشركاء والإشعارات (على سبيل المثال ، يحتاج المستخدمون إلى اتخاذ إجراء - تأكيد أو تنشيط البطاقة). في الوقت الحالي ، تبدو النتيجة كما يلي:
إضافة عنصر واجهة إلى المشروع
مثل الميزات الإضافية المماثلة الأخرى ، تتم إضافة الأداة كإمتداد للمشروع الرئيسي. بمجرد الإضافة ، قام Xcode بإنشاء رمز عنصر واجهة المستخدم والفئات الأساسية الأخرى. هذا هو المكان الذي تنتظرنا فيه الميزة الأولى المثيرة للاهتمام - بالنسبة لمشروعنا ، لم يتم تجميع هذا الرمز ، حيث تم إدراج بادئة تلقائيًا في أحد الملفات في أسماء الفئات (نعم ، تلك البادئات Obj-C نفسها!) ، ولكن ليس في الملفات التي تم إنشاؤها. كما يقول المثل ، ليس الآلهة هم من يحرقون الأواني ، على ما يبدو ، لم تتفق الفرق المختلفة داخل Apple فيما بينها. دعونا نأمل أن يقوموا بإصلاحه لنسخة الإصدار. من أجل تخصيص بادئة مشروعك ، في File Inspector للهدف الرئيسي للتطبيق ، قم بملء حقل Class Prefix .
بالنسبة لأولئك الذين تابعوا أخبار WWDC ، ليس سراً أن تنفيذ الحاجيات ممكن فقط باستخدام SwiftUI. هناك نقطة مثيرة للاهتمام وهي أنه بهذه الطريقة تفرض Apple تحديثًا لتقنياتها: حتى إذا كان التطبيق الرئيسي مكتوبًا باستخدام UIKit ، فعندئذٍ ، إذا سمحت ، SwiftUI فقط. من ناحية أخرى ، هذه فرصة جيدة لتجربة إطار عمل جديد لكتابة ميزة ، وفي هذه الحالة يتلاءم بشكل مريح مع العملية - لا توجد تغييرات في الحالة ، ولا تنقل ، تحتاج فقط إلى إعلان واجهة مستخدم ثابتة. هذا ، إلى جانب الإطار الجديد ، ظهرت قيود جديدة أيضًا ، لأن عناصر واجهة المستخدم القديمة في Today يمكن أن تحتوي على المزيد من المنطق والرسوم المتحركة.
أحد الابتكارات الرئيسية في SwiftUI هو القدرة على المعاينة دون تشغيلها على جهاز محاكاة أو جهاز ( معاينة ). إنه شيء رائع ، لكن لسوء الحظ ، في المشاريع الكبيرة (في مشاريعنا - 400 ألف سطر من التعليمات البرمجية) تعمل ببطء شديد حتى على أجهزة MacBooks العلوية ، فهي أسرع في التشغيل على الجهاز. البديل عن ذلك هو أن يكون لديك مشروع فارغ أو ملعب في متناول اليد للنماذج الأولية السريعة.
تصحيح الأخطاء متاح أيضًا مع مخطط Xcode مخصص. في جهاز المحاكاة ، التصحيح غير مستقر حتى في الإصدار Xcode 12 beta 6 ، لذلك من الأفضل التبرع بأحد أجهزة الاختبار والترقية إلى iOS 14 واختباره. كن مستعدًا لأن هذا الجزء لن يعمل كما هو متوقع في إصدارات الإصدار.
واجهه المستخدم
يمكن للمستخدم الاختيار من بين أنواع مختلفة ( WidgetFamily ) من ثلاثة أحجام - صغير ، متوسط ، كبير .
للتسجيل ، يجب تحديد المدعوم بشكل صريح:
struct CardListWidget: Widget {
public var body: some WidgetConfiguration {
IntentConfiguration(kind: “CardListWidgetKind”,
intent: DynamicMultiSelectionIntent.self,
provider: CardListProvider()) { entry in
CardListEntryView(entry: entry)
}
.configurationDisplayName(" ")
.description(", ")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
قررت أنا وفريقي التمسك بالبطاقات الصغيرة والمتوسطة - عرض بطاقة مفضلة واحدة لعنصر واجهة مستخدم صغير أو 4 بطاقة متوسطة.
تتم إضافة عنصر واجهة المستخدم إلى سطح المكتب من مركز التحكم ، حيث يختار المستخدم النوع الذي يحتاجه:
تخصيص لون الزر "إضافة عنصر واجهة مستخدم" باستخدام Assets.xcassets -> AccentColor ، اسم الأداة مع وصف أيضًا (رمز المثال أعلاه).
إذا واجهت قيودًا على عدد طرق العرض المدعومة ، يمكنك توسيعها باستخدام WidgetBundle :
@main
struct WalletBundle: WidgetBundle {
@WidgetBundleBuilder
var body: some Widget {
CardListWidget()
MySecondWidget()
}
}
نظرًا لأن الأداة تعرض لقطة من بعض الحالات ، فإن الاحتمال الوحيد لتفاعل المستخدم هو التبديل إلى التطبيق الرئيسي بالنقر فوق بعض العناصر أو عنصر واجهة المستخدم بالكامل. لا توجد رسوم متحركة أو تنقل أو انتقالات إلى طرق عرض أخرى . ولكن من الممكن إسقاط رابط لموضع معين في التطبيق الرئيسي. في هذه الحالة ، بالنسبة لعنصر واجهة مستخدم صغير ، تكون المنطقة القابلة للنقر هي المنطقة بأكملها ، وفي هذه الحالة نستخدم طريقة عنوان URL للقطعة (_ :) . بالنسبة إلى النقرات المتوسطة والكبيرة ، تتوفر نقرات العرض ، وستساعدنا بنية الارتباط من SwiftUI في ذلك .
Link(destination: card.url) {
CardView(card: card)
}
تحول العرض النهائي لعنصر واجهة المستخدم بحجمين على النحو التالي:
عند تصميم واجهة الأداة ، يمكن أن تساعد القواعد والمتطلبات التالية (وفقًا لإرشادات Apple):
- ركز القطعة على فكرة ومشكلة واحدة ، ولا تحاول تكرار جميع وظائف التطبيق.
- اعرض مزيدًا من المعلومات بناءً على الحجم ، بدلاً من مجرد قياس المحتوى.
- عرض المعلومات الديناميكية التي قد تتغير على مدار اليوم. لا يُرحب بالمتطرفات في شكل معلومات ومعلومات ثابتة تمامًا تتغير كل دقيقة.
- يجب أن توفر الأداة المعلومات ذات الصلة للمستخدمين ، وليس مجرد طريقة أخرى لفتح التطبيق.
تم تخصيص المظهر. الخطوة التالية هي اختيار البطاقات التي ستظهر للمستخدم وكيف. يمكن أن يكون هناك أكثر من أربع بطاقات بوضوح. لنفكر في عدة خيارات:
- السماح للمستخدم باختيار البطاقات. من ، إن لم يكن هو ، يعرف أي البطاقات أكثر أهمية!
- إظهار آخر الخرائط المستخدمة.
- قم بعمل خوارزمية أكثر ذكاءً ، مع التركيز ، على سبيل المثال ، على الوقت واليوم من الأسبوع والإحصائيات (إذا ذهب المستخدم إلى متجر فواكه بالقرب من المنزل في أيام الأسبوع وذهب إلى هايبر ماركت في عطلات نهاية الأسبوع ، فيمكنك مساعدة المستخدم في هذه اللحظة وإظهار البطاقة المطلوبة)
كجزء من النموذج الأولي ، استقرنا على الخيار الأول لكي نجرب في نفس الوقت القدرة على تكوين المعلمات مباشرة على الأداة. لا داعي لعمل شاشة خاصة داخل التطبيق. ومع ذلك ، هل المستخدمون ، كما يقولون ، لديهم خبرة كافية للعثور على هذه الإعدادات؟
إعدادات القطعة المخصصة
يتم إنشاء الإعدادات باستخدام النوايا (مرحبًا بمطوري Android) - عند إنشاء عنصر واجهة مستخدم جديد ، تتم إضافة ملف intent تلقائيًا إلى المشروع. سيقوم منشئ الكود بإعداد فئة موروثة من INIntent ، والتي تعد جزءًا من إطار عمل SiriKit . تحتوي معلمات intent على الخيار السحري "Intent مؤهل للأدوات" . تتوفر عدة أنواع من المعلمات ، ويمكنك تخصيص الأنواع الفرعية الخاصة بك. نظرًا لأن البيانات الموجودة في حالتنا هي قائمة ديناميكية ، فقد قمنا أيضًا بتعيين العنصر "يتم توفير الخيارات ديناميكيًا" .
بالنسبة لأنواع مختلفة من عناصر واجهة المستخدم ، قم بتعيين الحد الأقصى لعدد العناصر في القائمة - للصغير 1 ، وللوسط 4.
يتم استخدام هذا النوع من النية بواسطة عنصر واجهة المستخدم كمصدر بيانات.
بعد ذلك ، يجب وضع فئة الهدف المكونة في تكوين IntentConfiguration .
struct CardListWidget: Widget {
public var body: some WidgetConfiguration {
IntentConfiguration(kind: WidgetConstants.widgetKind,
intent: DynamicMultiSelectionIntent.self,
provider: CardListProvider()) { entry in
CardListEntryView(entry: entry)
}
.configurationDisplayName(" ")
.description(", .")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
إذا لم تكن إعدادات المستخدم مطلوبة ، فهناك بديل في شكل فئة StaticConfiguration ، والتي تعمل دون تحديد نية.
العنوان والوصف قابلين للتعديل على شاشة الإعدادات.
يجب احتواء اسم عنصر واجهة المستخدم في سطر واحد ، وإلا فسيتم اقتطاعه. في الوقت نفسه ، يختلف الطول المسموح به لشاشة الإضافة وإعدادات عنصر واجهة المستخدم.
أمثلة على الحد الأقصى لطول الاسم لبعض الأجهزة:
iPhone 11 Pro Max
28
21
iPhone 11 Pro
25
19
iPhone SE
24
19
الوصف متعدد الأسطر. في حالة وجود نص طويل جدًا في الإعدادات ، يمكن تمرير المحتوى. ولكن في شاشة الإضافة ، يتم ضغط معاينة عنصر واجهة المستخدم أولاً ، ثم يحدث شيء فظيع للتخطيط.
يمكنك أيضا تغيير لون الخلفية وقيم WidgetBackground المعلمات وAccentColor - افتراضيا هم بالفعل في الأصول . إذا لزم الأمر ، يمكن إعادة تسميتها في تكوين عنصر واجهة المستخدم في إنشاء الإعدادات في مجموعة Asset Catalog Compiler - Options في حقلي Widget Background Color و Global Accent Color Name على التوالي.
يمكن إخفاء بعض المعلمات (أو إظهارها) بناءً على القيمة المحددة في معلمة أخرى عبر إعداد العلاقة .
وتجدر الإشارة إلى أن واجهة المستخدم لتحرير المعلمة تعتمد على نوعها. على سبيل المثال ، إذا حددنا Boolean ، فسنرى UISwitch ، وإذا كان Integer ، فسيكون لدينا بالفعل خيارين: الإدخال عبر UITextfield أو التغيير خطوة بخطوة عبر UIStepper .
التفاعل مع التطبيق الرئيسي.
تم تكوين الحزمة ، ويبقى تحديد المكان الذي ستأخذ منه النية البيانات الحقيقية. الجسر مع التطبيق الرئيسي في هذه الحالة هو ملف في المجموعة العامة ( مجموعات التطبيقات ). يكتب التطبيق الرئيسي ، ويقرأ القطعة.
تُستخدم الطريقة التالية للحصول على عنوان URL للمجموعة العامة:
FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: “group.ru.yourcompany.yourawesomeapp”)
نقوم بحفظ جميع المرشحين ، حيث سيتم استخدامها من قبل المستخدم في الإعدادات كقاموس للاختيار.
بعد ذلك ، يجب أن يكتشف نظام التشغيل أن البيانات قد تم تحديثها ، ولهذا نسمي:
WidgetCenter.shared.reloadAllTimelines()
// WidgetCenter.shared.reloadTimelines(ofKind: "kind")
نظرًا لأن استدعاء الطريقة سيعيد تحميل محتوى عنصر واجهة المستخدم والجدول الزمني بأكمله ، استخدمه عندما يتم تحديث البيانات بالفعل حتى لا تفرط في تحميل النظام.
تحديث البيانات
من أجل رعاية بطارية جهاز المستخدم ، فكرت Apple في آلية لتحديث البيانات الموجودة على عنصر واجهة مستخدم باستخدام جدول زمني - آلية لإنشاء لقطات . المطور لا يتم تحديث مباشرة أو إدارة عرض ، ولكن بدلا من ذلك يوفر الجدول الزمني، مسترشدة فيه نظام التشغيل وقطع لقطات في الخلفية.
يتم التحديث في الأحداث التالية:
- استدعاء WidgetCenter.shared.reloadAllTimelines () المستخدم سابقًا
- عندما يضيف المستخدم عنصر واجهة مستخدم إلى سطح المكتب
- عند تحرير الإعدادات.
أيضًا ، لدى المطور ثلاثة أنواع من السياسات لتحديث الجداول الزمنية (TimelineReloadPolicy):
atEnd - التحديث بعد عرض اللقطة الأخيرة
مطلقًا - التحديث فقط في حالة إجراء مكالمة إجبارية
بعد (_ :) - التحديث بعد فترة زمنية معينة.
في حالتنا ، يكفي أن نطلب من النظام أن يأخذ لقطة واحدة حتى يتم تحديث بيانات البطاقة في التطبيق الرئيسي:
struct CardListProvider: IntentTimelineProvider {
public typealias Intent = DynamicMultiSelectionIntent
public typealias Entry = CardListEntry
public func placeholder(in context: Context) -> Self.Entry {
return CardListEntry(date: Date(), cards: testData)
}
public func getSnapshot(for configuration: Self.Intent, in context: Self.Context, completion: @escaping (Self.Entry) -> Void) {
let entry = CardListEntry(date: Date(), cards: testData)
completion(entry)
}
public func getTimeline(for configuration: Self.Intent, in context: Self.Context, completion: @escaping (Timeline<Self.Entry>) -> Void) {
let cards: [WidgetCard]? = configuration.cards?.compactMap { card in
let id = card.identifier
let storedCards = SharedStorage.widgetRepository.restore()
return storedCards.first(where: { widgetCard in widgetCard.id == id })
}
let entry = CardListEntry(date: Date(), cards: cards ?? [])
let timeline = Timeline(entries: [entry], policy: .never)
completion(timeline)
}
}
struct CardListEntry: TimelineEntry {
public let date: Date
public let cards: [WidgetCard]
}
سيكون الخيار الأكثر مرونة مفيدًا في حالة استخدام خوارزمية تلقائية لاختيار البطاقات اعتمادًا على يوم الأسبوع والوقت.
بشكل منفصل ، تجدر الإشارة إلى عرض عنصر واجهة المستخدم إذا كان في كومة من الأدوات ( Smart Stack ). في هذه الحالة ، يمكننا استخدام خيارين لإدارة الأولويات: اقتراحات Siri أو عن طريق تعيين قيمة الصلة للجدول الزمني - الدخول مع نوع TimelineEntryRelibility . يحتوي TimelineEntryRelibility على معلمتين:
الدرجة - أولوية اللقطة الحالية بالنسبة إلى اللقطات الأخرى ؛
المدة هي الوقت حتى تظل الأداة ذات صلة ويمكن للنظام وضعها في الموضع العلوي في المكدس.
تمت مناقشة كلتا الطريقتين ، بالإضافة إلى خيارات التكوين لعنصر واجهة المستخدم ، بالتفصيل في جلسة WWDC .
تحتاج أيضًا إلى التحدث عن كيفية الحفاظ على تحديث عرض التاريخ والوقت. نظرًا لأنه لا يمكننا تحديث محتوى الأداة بشكل منتظم ، فقد تمت إضافة العديد من الأنماط لمكون النص. عند استخدام أحد الأنماط ، يقوم النظام تلقائيًا بتحديث محتوى المكون أثناء وجود عنصر واجهة المستخدم على الشاشة. ربما في المستقبل ، سيتم تمديد نفس النهج لمكونات SwiftUI الأخرى.
يدعم النص الأنماط التالية:
نسبي- فرق التوقيت بين التاريخ الحالي والتاريخ المحدد. تجدر الإشارة هنا: إذا تم تحديد التاريخ في المستقبل ، فسيبدأ العد التنازلي ، وبعد ذلك يظهر التاريخ من لحظة وصوله إلى الصفر. سيكون نفس السلوك بالنسبة للنمطين التاليين ؛
الإزاحة - على غرار السابقة ، ولكن هناك إشارة في شكل بادئة بـ ± ؛
جهاز ضبط الوقت - التناظرية لجهاز ضبط الوقت.
التاريخ - عرض التاريخ ؛
الوقت - عرض الوقت .
بالإضافة إلى ذلك ، من الممكن عرض الفاصل الزمني بين التواريخ ببساطة عن طريق تحديد الفاصل الزمني.
let components = DateComponents(minute: 10, second: 0)
let futureDate = Calendar.current.date(byAdding: components, to: Date())!
VStack {
Text(futureDate, style: .relative)
.multilineTextAlignment(.center)
Text(futureDate, style: .offset)
.multilineTextAlignment(.center)
Text(futureDate, style: .timer)
.multilineTextAlignment(.center)
Text(Date(), style: .date)
.multilineTextAlignment(.center)
Text(Date(), style: .time)
.multilineTextAlignment(.center)
Text(Date() ... futureDate)
.multilineTextAlignment(.center)
}
معاينة القطعة
عند عرضها لأول مرة ، سيتم فتح الأداة في وضع المعاينة ، لذلك نحتاج إلى إرجاع TimeLineEntry في العنصر النائب (في طريقة :). في حالتنا ، يبدو كالتالي:
func placeholder(in context: Context) -> Self.Entry {
return CardListEntry(date: Date(), cards: testData)
}
بعد ذلك ، يتم تطبيق المعدل المنقح (السبب :) مع المعلمة النائب على العرض . في هذه الحالة ، يتم عرض العناصر الموجودة على الأداة بشكل ضبابي.
يمكننا إزالة هذا التأثير من بعض العناصر باستخدام معدّل () unredacted .
تشير الوثائق أيضًا إلى أن استدعاء طريقة العنصر النائب (في :) متزامن ويجب أن تعود النتيجة بأسرع ما يمكن ، على عكس getSnapshot (in: complete :) و getTimeline (in: complete :)
عناصر التقريب
في الإرشادات ، يوصى بمطابقة تقريب العناصر مع تقريب عنصر واجهة المستخدم ؛ لهذا ، تمت إضافة بنية ContainerRelativeShape في iOS 14 ، مما يسمح لك بتطبيق شكل الحاوية على العرض.
.clipShape(ContainerRelativeShape())
دعم الهدف جيم
إذا كنت بحاجة إلى إضافة كود Objective-C إلى عنصر واجهة المستخدم (على سبيل المثال ، قمنا بكتابة إنشاء صور الباركود عليها) ، فكل شيء يحدث بالطريقة القياسية عن طريق إضافة رأس توصيل Objective-C. كانت المشكلة الوحيدة التي واجهتنا هي أنه عند الإنشاء ، توقف Xcode عن رؤية ملفات الهدف التي تم إنشاؤها تلقائيًا ، لذلك أضفناها أيضًا إلى رأس التجسير :
#import "DynamicCardSelectionIntent.h"
#import "CardSelectionIntent.h"
#import "DynamicMultiSelectionIntent.h"
حجم التطبيق
تم إجراء الاختبار على Xcode 12 beta 6
بدون عنصر واجهة مستخدم: 61.6 ميجا بايت
مع عنصر واجهة مستخدم: 62.2 ميجا بايت وسوف
ألخص النقاط الرئيسية التي تمت مناقشتها في المقالة:
- تعد الأدوات المصغرة طريقة رائعة للتعرف على SwiftUI في الممارسة العملية. قم بإضافتها إلى مشروعك حتى إذا كان الحد الأدنى للإصدار المدعوم أقل من iOS 14.
- يتم استخدام WidgetBundle لزيادة عدد عناصر واجهة المستخدم المتاحة ، وهنا مثال رائع على عدد الأدوات المختلفة التي تمتلكها ApolloReddit.
- سيساعد IntentConfiguration أو StaticConfiguration في إضافة إعدادات مخصصة على عنصر واجهة المستخدم نفسه إذا لم تكن هناك حاجة إلى إعدادات مخصصة.
- سيساعد مجلد مشترك على نظام الملفات في مجموعات التطبيقات المشتركة على مزامنة البيانات مع التطبيق الرئيسي.
- يمكن للمطور الاختيار من بين عدة سياسات لتحديث الجدول الزمني (في النهاية ، أبدًا ، بعد (_ :)).
في هذا الصدد ، يمكن اعتبار المسار الشائك لتطوير عنصر واجهة مستخدم على إصدارات تجريبية من Xcode مكتملاً ، ولم يتبق سوى خطوة واحدة بسيطة - لإجراء مراجعة في متجر التطبيقات.
ملاحظة: لقد اجتاز الإصدار الذي يحتوي على عنصر واجهة المستخدم الاعتدال وهو متوفر الآن للتنزيل في App Store!
أشكركم على القراءة حتى النهاية ، سأكون سعيدًا بالاقتراحات والتعليقات. يرجى إجراء استطلاع قصير لمعرفة مدى شعبية الأدوات المصغّرة بين المستخدمين والمطورين.