Neomorphism باستخدام SwiftUI. الجزء 2: ما الذي يمكن عمله بإمكانية الوصول؟

تحية للجميع! تحسبًا لبدء الدورة المتقدمة "IOS Developer" ، ننشر ترجمة للجزء الثاني من المقالة حول الشكل الجديد باستخدام SwiftUI ( اقرأ الجزء الأول ).





مظهر داكن


قبل أن نبدأ في فهم كيف يمكننا تحسين توفر الشكل الجديد ، دعنا نرى كيف يمكننا العمل مع التأثير لإنشاء أنماط أخرى مثيرة للاهتمام.

قم أولاً بإضافة لونين إضافيين إلى الامتداد Colorبحيث يكون لدينا العديد من القيم الثابتة في متناول اليد:

static let darkStart = Color(red: 50 / 255, green: 60 / 255, blue: 65 / 255)
static let darkEnd = Color(red: 25 / 255, green: 25 / 255, blue: 30 / 255)

يمكننا استخدامها كخلفية ContentViewلاستبدال القائمة الموجودة Color.white، كما هو موضح هنا:

var body: some View {
    ZStack {
        LinearGradient(Color.darkStart, Color.darkEnd)

SimpleButtonStyleيبدو لنا الآن خارج المكان ، لأنه يفرض نمطًا مشرقًا على خلفية داكنة. لذلك ، سنقوم بإنشاء نمط مظلم جديد يعمل بشكل أفضل هنا ، ولكن هذه المرة سنقوم بتقسيمه إلى جزأين: عرض خلفية يمكننا استخدامه في أي مكان ، ونمط زر يلفه بمعدلات الحشو ومحتويات الشكل. سيعطينا هذا المزيد من المرونة ، كما سترى لاحقًا.

ستتيح لنا طريقة عرض الخلفية الجديدة التي سنضيفها تحديد أي شكل لتأثيرنا البصري ، لذلك لم نعد مرتبطين بالدوائر. سيتابع أيضًا ما إذا كان سيحدث تأثيرًا محدبًا (داخليًا أو خارجيًا) ، اعتمادًا على الخاصية isHighlightedالتي يمكننا تغييرها من الخارج.

سنبدأ مع أبسط ، باستخدام نهج تعديل الظل الوجه للحصول على تأثير مقعر. أضف هذا الهيكل:

struct DarkBackground<S: Shape>: View {
    var isHighlighted: Bool
    var shape: S

    var body: some View {
        ZStack {
            if isHighlighted {
                shape
                    .fill(Color.darkEnd)
                    .shadow(color: Color.darkStart, radius: 10, x: 5, y: 5)
                    .shadow(color: Color.darkEnd, radius: 10, x: -5, y: -5)

            } else {
                shape
                    .fill(Color.darkEnd)
                    .shadow(color: Color.darkStart, radius: 10, x: -10, y: -10)
                    .shadow(color: Color.darkEnd, radius: 10, x: 10, y: 10)
            }
        }
    }
}

التعديل هو أنه عند الضغط على الزر ، ينخفض ​​حجم الظل - نستخدم مسافة 5 نقاط بدلاً من 10.

ثم يمكننا لفها بمساعدة DarkButtonStyleالحشو والشكل ، كما هو موضح هنا:

struct DarkButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .padding(30)
            .contentShape(Circle())
            .background(
                DarkBackground(isHighlighted: configuration.isPressed, shape: Circle())
            )
    }
}

أخيرًا ، يمكننا تطبيق هذا على زرنا ContentViewعن طريق تغييره ButtonStyle():

.buttonStyle(DarkButtonStyle())

دعونا نرى ما حدث - على الرغم من أنه ليس لدينا الكثير من التعليمات البرمجية ، أعتقد أن النتيجة تبدو جيدة بما يكفي.



بعض التجارب


حان الوقت لتجربة التأثير ، لأنه سيساعدك على فهم أفضل لما يستطيع SwiftUI تحديده.

على سبيل المثال ، يمكننا إنشاء زر محدب سلس بإضافة تدرج خطي إليه وقلبه عند الضغط عليه:

if isHighlighted {
    shape
        .fill(LinearGradient(Color.darkEnd, Color.darkStart))
        .shadow(color: Color.darkStart, radius: 10, x: 5, y: 5)
        .shadow(color: Color.darkEnd, radius: 10, x: -5, y: -5)

} else {
    shape
        .fill(LinearGradient(Color.darkStart, Color.darkEnd))
        .shadow(color: Color.darkStart, radius: 10, x: -10, y: -10)
        .shadow(color: Color.darkEnd, radius: 10, x: 10, y: 10)
}

إذا قمت بتشغيل هذا ، سترى أن الزر يتحرك بسلاسة لأعلى ولأسفل عند الضغط عليه وتحريره. أجد أن الرسم المتحرك يشتت الانتباه قليلاً ، لذا أوصي بتعطيله عن طريق إضافة هذا المعدل إلى الطريقة makeBody()من DarkButtonStyle، بعد وجود المُعدِّل هناك background():

.animation(nil)



هذا التأثير لزر الوسادة ساحر ، ولكن إذا كنت تخطط لاستخدامه ، أنصحك بتجربة التغييرات الثلاثة التالية لجعل الزر بارزًا أكثر قليلاً.

أولاً ، على الرغم من حقيقة أن هذا يتناقض مع مبدأ التباين المنخفض في التصميم النيومورفي ، إلا أنني سأستبدل الرمز الرمادي بأخرى بيضاء لجعله بارزًا. لذا ، ContentViewستحتاج إلى ما يلي:

Image(systemName: "heart.fill")
    .foregroundColor(.white)

ثانيًا ، إذا قمت بإضافة تراكب للزر في الحالة المضغوطة ، فإن هذا لن يجعله يبدو فقط وكأنه زر حقيقي حقيقي يتم الضغط عليه بالتساوي ، ولكنه سيساعد أيضًا في تمييز حالة الضغط عنه عن الحالة غير المضغوطة.

لتنفيذ هذا، تحتاج إلى إدراج التعديل overlay()بعد fill()عندما isHighlightedكان صحيحا، كما هنا:

if isHighlighted {
    shape
        .fill(LinearGradient(Color.darkEnd, Color.darkStart))
        .overlay(shape.stroke(LinearGradient(Color.darkStart, Color.darkEnd), lineWidth: 4))
        .shadow(color: Color.darkStart, radius: 10, x: 5, y: 5)
        .shadow(color: Color.darkEnd, radius: 10, x: -5, y: -5)



للحصول على مظهر أكثر وضوحًا ، يمكنك إزالة معدلين shadow()للحالة المضغوطة التي تركز على التراكب بخلاف ذلك.

ثالثًا ، يمكنك أيضًا إضافة تراكب إلى الحالة غير المضغوطة ، فقط لتمييز أنه زر. ضعه فورًا fill()، مثل:

} else {
    shape
        .fill(LinearGradient(Color.darkStart, Color.darkEnd))
        .overlay(shape.stroke(Color.darkEnd, lineWidth: 4))
        .shadow(color: Color.darkStart, radius: 10, x: -10, y: -10)
        .shadow(color: Color.darkEnd, radius: 10, x: 10, y: 10)
}



إضافة نمط التبديل


تتمثل إحدى مزايا فصل نمط زر عن نمط خلفية غير مورفي في أنه يمكننا الآن إضافة نمط تبديل باستخدام نفس التأثير. وهذا يعني إنشاء بنية جديدة متوافقة مع البروتوكول ToggleStyleتشبه ButtonStyle، باستثناء ما يلي:

  1. نحتاج إلى القراءة configuration.isOnلتحديد ما إذا كان المفتاح قيد التشغيل.
  2. نحن بحاجة إلى توفير زر للتعامل مع عملية التبديل ، أو على الأقل شيء من هذا القبيل onTapGesture()أو شيء من هذا القبيل .

أضف هذه البنية إلى مشروعك:

struct DarkToggleStyle: ToggleStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        Button(action: {
            configuration.isOn.toggle()
        }) {
            configuration.label
                .padding(30)
                .contentShape(Circle())
        }
        .background(
            DarkBackground(isHighlighted: configuration.isOn, shape: Circle())
        )
    }
}

نريد أن نضع أحدهم ContentViewبحيث يمكنك اختباره بنفسك ، لذا ابدأ بإضافة هذه الخاصية:

@State private var isToggled = false

ثم لف الزر الموجود VStackبمسافة تساوي 40 وضعه أدناه:

Toggle(isOn: $isToggled) {
    Image(systemName: "heart.fill")
        .foregroundColor(.white)
}
.toggleStyle(DarkToggleStyle())

ContentViewيجب أن يبدو الهيكل الخاص بك مثل هذا:

struct ContentView: View {
    @State private var isToggled = false

    var body: some View {
        ZStack {
            LinearGradient(Color.darkStart, Color.darkEnd)

            VStack(spacing: 40) {
                Button(action: {
                    print("Button tapped")
                }) {
                    Image(systemName: "heart.fill")
                        .foregroundColor(.white)
                }
                .buttonStyle(DarkButtonStyle())

                Toggle(isOn: $isToggled) {
                    Image(systemName: "heart.fill")
                        .foregroundColor(.white)
                }
                .toggleStyle(DarkToggleStyle())
            }
        }
        .edgesIgnoringSafeArea(.all)
    }
}

هذا كل ما في الأمر - لقد طبقنا تصميمنا النيومورفي المشترك في مكانين!

تحسين إمكانية الوصول


كان لدينا الوقت الكافي للعب مع أنماط مختلفة من الأشكال الحديثة ، لكني الآن أريد أن أتطرق إلى مشاكل هذا التصميم: النقص الشديد في التباين يعني أن الأزرار وعناصر التحكم المهمة الأخرى لا تكفي للتميز عن محيطها ، مما يجعل من الصعب استخدام تطبيقاتنا للأشخاص الذين لديهم رؤية ناقصة.

هذه هي اللحظة التي ألاحظ فيها بعض سوء الفهم ، لذلك أود أن أقول بعض الأشياء مقدمًا:

  1. نعم ، أفهم أن أزرار Apple القياسية تبدو تمامًا مثل النص الأزرق ، وبالتالي فهي لا تشبه الأزرار المألوفة ، للوهلة الأولى على الأقل ، ولكن لها نسبة تباين عالية.
  2. « , , » — — « », , , - .
  3. - SwiftUI, , VoiceOver Apple.

لقد رأيت بالفعل كيف قمنا بتغيير الرمز الرمادي إلى الأبيض للحصول على تحسين التباين الفوري ، ولكن الأزرار والمفاتيح لا تزال بحاجة إلى مزيد من التباين إذا كنت تريد جعلها أكثر سهولة.

لذا ، سننظر في التغييرات التي يمكننا إجراؤها حتى تبرز العناصر حقًا.

أولاً ، أود أن تضيف لونين جديدين إلى امتدادنا:

static let lightStart = Color(red: 60 / 255, green: 160 / 255, blue: 240 / 255)
static let lightEnd = Color(red: 30 / 255, green: 80 / 255, blue: 120 / 255)

ثانيًا ، قم بتكرار النسخة الموجودة DarkBackgroundوقم بتسمية النسخة ColorfulBackground. سنتعامل معها في لحظة ، ولكن مرة أخرى ، نحتاج أولاً إلى القيام ببعض التحضير.

ثالثا، تكرار النمط الظلام زر والتبديل، تسميتها إلى ColorfulButtonStyleو ColorfulToggleStyle، ومن ثم جعلها استخدام واحدة جديدة ColorfulBackgroundكخلفية.

لذلك يجب أن تبدو مثل هذا:

struct ColorfulButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .padding(30)
            .contentShape(Circle())
            .background(
                ColorfulBackground(isHighlighted: configuration.isPressed, shape: Circle())
            )
            .animation(nil)
    }
}

struct ColorfulToggleStyle: ToggleStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        Button(action: {
            configuration.isOn.toggle()
        }) {
            configuration.label
                .padding(30)
                .contentShape(Circle())
        }
        .background(
            ColorfulBackground(isHighlighted: configuration.isOn, shape: Circle())
        )
    }
}

وأخيرًا ، عدّل الزر وانتقل ContentViewلاستخدام النمط الجديد:

Button(action: {
    print("Button tapped")
}) {
    Image(systemName: "heart.fill")
        .foregroundColor(.white)
}
.buttonStyle(ColorfulButtonStyle())

Toggle(isOn: $isToggled) {
    Image(systemName: "heart.fill")
        .foregroundColor(.white)
}
.toggleStyle(ColorfulToggleStyle())

يمكنك تشغيل التطبيق إذا كنت تريد ، ولكن هذا لا معنى له - لم يتغير في الواقع.

لإحضار نسختنا الملونة من الحياة ، سنقوم بتغيير المعدلات fill()والدول overlay()المضغوطة وغير المضغوطة. لذلك، عندما isHighlightedصحيحا، تغيير darkStartكل من darkEndل lightStartو lightEnd، مثل هذا:

if isHighlighted {
    shape
        .fill(LinearGradient(Color.lightEnd, Color.lightStart))
        .overlay(shape.stroke(LinearGradient(Color.lightStart, Color.lightEnd), lineWidth: 4))
        .shadow(color: Color.darkStart, radius: 10, x: 5, y: 5)
        .shadow(color: Color.darkEnd, radius: 10, x: -5, y: -5)

إذا قمت بتشغيل التطبيق مرة أخرى ، سترى أنه قد تحسن بالفعل بشكل ملحوظ: أصبحت الحالة المضغوطة الآن لون أزرق ساطع ، لذلك يصبح من الواضح عند الضغط على الأزرار وتكون المفاتيح نشطة. ولكن يمكننا القيام بشيء آخر - يمكننا إضافة نفس اللون حول الزر عندما لا يتم الضغط عليه ، مما يساعد على جذب الانتباه إليه.



للقيام بذلك ، قم بتغيير حالة overlay()عدم التأكيد الحالية إلى ما يلي:

.overlay(shape.stroke(LinearGradient(Color.lightStart, Color.lightEnd), lineWidth: 4))


لذا ، يجب أن يبدو نمط الزر النهائي كما يلي:

ZStack {
    if isHighlighted {
        shape
            .fill(LinearGradient(Color.lightEnd, Color.lightStart))
            .overlay(shape.stroke(LinearGradient(Color.lightStart, Color.lightEnd), lineWidth: 4))
            .shadow(color: Color.darkStart, radius: 10, x: 5, y: 5)
            .shadow(color: Color.darkEnd, radius: 10, x: -5, y: -5)
    } else {
        shape
            .fill(LinearGradient(Color.darkStart, Color.darkEnd))
            .overlay(shape.stroke(LinearGradient(Color.lightStart, Color.lightEnd), lineWidth: 4))
            .shadow(color: Color.darkStart, radius: 10, x: -10, y: -10)
            .shadow(color: Color.darkEnd, radius: 10, x: 10, y: 10)
    }
}

الآن قم بتشغيل التطبيق مرة أخرى ، وسترى ظهور حلقة زرقاء حول الأزرار والمفاتيح ، وعند النقر فوقها تكون مليئة باللون الأزرق - يمكن الوصول إليها بسهولة أكبر.



استنتاج


من الناحية العملية ، لن يكون لديك العديد من أنماط الأزرار المختلفة في مشروع واحد ، على الأقل إذا كنت لا ترغب في إحداث صداع للمستخدمين. لكن هذه مساحة مثيرة للاهتمام للتجريب ، وآمل أن تفهم هذه الفكرة من مقالتي ، لأنه يمكنك إنشاء تأثيرات جميلة حقًا دون تمزق ظهرك.

لقد قلت مرارًا أنه يجب عليك دائمًا مراقبة توفر التطبيق الخاص بك ، وهذا يعني أكثر من مجرد التأكد من عمل VoiceOver مع واجهة المستخدم الخاصة بك. تأكد من أن الأزرار تبدو تفاعلية ، وتأكد من أن تسميات النص والرموز الخاصة بك تحتوي على نسبة تباين كافية للخلفية (4.5: 1 على الأقل ، ولكن تميل إلى 7: 1) ، وتأكد من أن المناطق القابلة للنقر مريحة كبير (44x44 بكسل على الأقل).

ومن أجل السماء ، استخدم التصميم النيومورفي لتجربة وتوسيع معرفتك بـ SwiftUI ، ولكن لا تنس أبدًا أنه إذا ضحت بقابلية الاستخدام من أجل اتجاه جديد لتصميم الأزياء ، فلن تفوز بأي شيء.

يمكنك الحصول على كامل شفرة المصدر لهذا المشروع على جيثب .

اقرأ الجزء الأول.



تعلم المزيد عن الدورة.



All Articles