3. Bölüm: Swift'in Operatör Aşırı Yüklemesi ve Sonuç Oluşturucuları ile Otomatik Düzen DSL Yazma ️

Nov 26 2022
Bu makaleyi de buradan izleyebilirsiniz: Geçen sefer DSL'nin ana bölümünü oluşturduk, son kullanıcının kısıtlamaları aritmetik bir şekilde ifade etmesine izin verdik. Bu makalede şunları içerecek şekilde genişleteceğiz: İlk olarak, Bölüm 1'de tanımlanan LayoutAnchor protokolüne uyan bir çift çapayı tutmak için bir tür yaratıyoruz.

Bu makaleyi buradan da izleyebilirsiniz:

Geçen sefer DSL'nin ana bölümünü oluşturduk ve son kullanıcının kısıtlamaları aritmetik bir şekilde ifade etmesine izin verdik.

Bu yazıda şunları içerecek şekilde genişleteceğiz:

  • Kombine ankrajlar — boyut, merkez, yatay Kenarlar, dikey Kenarlar
  • Ekler — kullanıcıların birleştirilmiş kenar ankrajlarına ek yerleştirmelerine olanak tanır
  • Farklı ankrajların çıktılarını işlemek için Sonuç Oluşturucu, kolay kısıtlama gruplama ve etkinleştirmeye izin verir.

İlk önce, Bölüm 1'de tanımlanan LayoutAnchor protokolüne uyan bir çift çapa tutmak için bir tür oluşturuyoruz.

Burada, bir dizi çapa tutmak için LayoutBlock'u değiştirmeyi düşündüm. Ardından, bu tür 2 bloktan bir kısıtlama oluştururken, çapaları birbirine sıkıştırabilir ve çapaları birbirine sınırlayarak ve ilgili sabitleri/çarpanları geçirerek üzerlerinde yineleyebiliriz.

2 dezavantajı var:

  • Temel çapalara sahip tek ifadeler bile bir dizi kısıtlama döndürür. Bu, DSL'yi kullanıcıların bakış açısından karmaşıklaştırır.
  • Bir kullanıcı, tek bir çapa ile bileşik bir çapa (2 veya 4 çapa ile) deneyebilir ve kullanabilir. Ek çapaları göz ardı ederek bunu halledebiliriz. Derleyici herhangi bir uyarı üretmeyecektir. Ancak, bu işlemler ve sonuçta ortaya çıkan kısıtlamalar anlamsız olacaktır. Bu, son kullanıcı koduna sinir bozucu hatalar getirme potansiyeline sahiptir - kaçınmak istediğimiz bir şey!

Adım 11: LayoutAnchorPair protokolünü genişletin.

Bu uzantı, daha önce tanımlanan LayoutAnchor protokol uzantısıyla aynı amaca hizmet edecektir - temel türün yöntemlerini çağıran ve sonuçta ortaya çıkan kısıtlamaları döndüren bir sarmalayıcı görevi görür.

Buradaki temel fark, her yöntemin 2 LayoutAnchor türünün kısıtlamalarını birleştiren bir kısıtlama dizisi döndürmesidir.

LayoutAnchorPair'e geçirdiğimiz ankrajlar LayoutAnchor türleriyle sınırlı olduğundan, kolayca varsayılan bir uygulama sağlayabiliriz.

Ek olarak, sabitler yerine bu yöntemler, kısıtlamaların her birine farklı sabitler sağlama yeteneği sunan bir EdgeInsetPair alır.

Her yöntem, sabit1'i bağlantı1'i ve sabit2'yi bağlantı2'de kısıtlamak için eşler.

Adım 13: Somut LayoutAnchor türleri oluşturun.

Kısım 1 ve 2'de, varsayılan NSLayoutAnchors'ı LayoutAnchor protokolüne uygun hale getirdiğimiz için somut LayoutAnchor türleri oluşturmak zorunda değildik. Ancak burada, AnchorPair protokolüne uyan kendi çapalarımızı sağlamamız gerekiyor.

Daha önce kullanılan tip takma adlara ilişkin bir hatırlatma:

EdgeInsetPair protokolünü karşılayan iç içe bir tür tanımlıyoruz. Bu, AnchorPair ile ilişkili tür gereksinimini karşılar - Ekler. Bu protokole bağlı beton tipi, çalışma aşırı yüklemesi sırasında ekleri ayarlamak için kullanılacaktır.

LayoutAnchorPair ve EdgeInsetPair protokolüne uymak için hesaplanan özellikleri kullanıyoruz. Bu hesaplanan özellikler, LayoutAnchorPair ve EdgeInsetPair'in dahili özelliklerini döndürür.

Burada, iç metin türü tarafından sağlanan sabitlerin bu bağlantı çiftinde tanımlanan bağlantı noktalarıyla eşleşmesini sağlamak önemlidir. Özellikle son adımda tanımlanan uzantı bağlamında, burada sabit1 çapa1'i sınırlamak için kullanılır .

Bu "jenerik" protokol, tüm bağlantı çiftleri için geçerli olan bir protokol uzantısı tanımlamamıza izin verir. Yukarıda tartışılan kurala uymamız şartıyla. Aynı zamanda, uzantının dışında daha anlamlı çapa özel etiketler - alt/üst - kullanabiliriz. Operatör aşırı yüklerini tanımlarken olduğu gibi.

Alternatif bir çözüm, tüm türler için ayrı uzantılara sahip olmayı gerektirir - bu, çapa sayısı sınırlı olduğu için çok da kötü değildir. Ama burada tembeldim ve soyut bir çözüm yaratmaya çalıştım. Her iki durumda da bu, kitaplığın içinde bulunan ve gelecekte değişiklikleri bozmadan değiştirilebilen bir uygulama ayrıntısıdır.

Farklı bir tasarımın optimal olacağına inanıyorsanız lütfen yorum bırakın.

Daha fazla mimari karar noktası:

  • Ekler, başlatıldıkları ve bir bağlantının dışında kullanıldıkları için ayrı bir tür olmalıdır.
  • İç içe türler, ad alanını korumak ve temiz tutmak için kullanılırken, aynı zamanda bir Inset türü uygulamasının belirli PairAnchor uygulamasına bağlı olduğu gerçeğini vurgular.

Not: İç metin eklemek, bir ifadeye sabit eklemekle aynı şey değildir.

EdgePair arabiriminin bir parçası olarak döndürmeden önce en üst sabite bir eksi işleci eklediğimizi fark etmişsinizdir . Benzer şekilde, XAxisAnchorPair uygulamaları, sondaki çapaya bir eksi ekler. Sabitleri tersine çevirmek, eklerin her bir kenarı aynı yönde kaydırmak yerine ek olarak işlev göreceği anlamına gelir.

Birinci ve ikinci resimde kırmızı görünüm 200 ile sınırlandırılmıştır.

Soldaki resimde, mavi görünümün tüm kenar çapaları, kırmızı görünümünki artı 40'a eşit olacak şekilde ayarlanmıştır. Bu, mavi görünümün aynı boyutta olmasına ancak her iki eksen boyunca 40 kaydırılmasına neden olur. Bu, kısıtlamalar açısından mantıklı olsa da, bu kendi başına yaygın bir işlem değildir.

Bir görünümün etrafına iç metinler ayarlamak veya bir görünümün etrafına dolgu eklemek çok daha yaygındır. Bu nedenle, birleşik aktörler için bir API sağlama bağlamında daha anlamlıdır.

Doğru görüntüde, bu DSL'yi kullanarak mavi görünümü kırmızı görünüme ve 40'lık bir eke eşit olacak şekilde ayarladık. Bu, yukarıda açıklanan sabitlerin tersine çevrilmesiyle elde edilir.

Adım 14: Kompozit ankrajları başlatmak için View & LayoutGuide'ı genişletin.

Daha önce yaptığımız gibi View ve LayoutGuide türlerini, çağrıldıklarında LayoutBlock'ları başlatan hesaplanmış özelliklerle genişletiyoruz.

Adım 15: Kenar ekleri olan ifadelere izin vermek için +/- işleçlerini aşırı yükleyin.

Yatay kenar ve dikey kenar ankrajları için kullanıcının ekleri belirleyebilmesini istiyoruz. Bunu başarmak için UIEdgeInsets türünü genişletiyoruz çünkü bu DSL'nin çoğu kullanıcısı zaten aşinadır.

Uzantı, yalnızca üst/alt veya sol/sağ eklerle başlatmaya izin verir; geri kalanı varsayılan olarak 0'dır.

Ayrıca EdgeInset'leri depolamak için LayoutBlock'a yeni bir özellik eklememiz gerekiyor.

Sonra girişler için işleçleri aşırı yüklüyoruz: LayoutBlock with UIEdgeInsets .

Kullanıcı tarafından sağlanan UIEdgeInsets örneğini , somut LayoutAnchorPair parçası olarak tanımlanan ilgili iç içe tiple eşliyoruz .

UIEdgeInset türünün bir parçası olarak kullanıcı tarafından iletilen fazladan veya yanlış parametreler dikkate alınmaz.

Adım 15: Kısıtlama ilişkilerini tanımlamak için Karşılaştırma Operatörlerini Aşırı Yükleyin.

İlke öncekiyle aynı kalır. LayoutBlocks ve LayoutAnchorPair girişleri için ilişki operatörlerini aşırı yüklüyoruz.

Bir kullanıcı bir çift kenar eki sağlarsa, bunları kullanırız, aksi takdirde LayoutBlocks sabitlerinden bir çift genel ek oluştururuz. Genel ek yapı bir sarmalayıcıdır, diğer eklerin aksine kenarlardan birini olumsuzlamaz.

Adım 16: Dimension LayoutAnchorPair

Tıpkı tek boyutlu bir çapa gibi, bir çift boyut çapası ( widthAnchor ve heightAnchor ) sabitlerle sınırlandırılabilir. Bu nedenle, bu kullanım durumunu ele almak için ayrı operatör aşırı yüklemeleri sağlamalıyız.

  • DSL kullanıcısının hem yüksekliği hem de genişliği aynı sabite sabitleyerek bir kare oluşturmasına izin verin.
  • DSL kullanıcısının SizeAnchorPair'i bir CGSize türüne sabitlemesine izin verin — görünümler kare olmadığından çoğu durumda daha mantıklıdır.

Adım 17: [NSLayoutConstraint] ve NSLayoutConstraint türlerini işlemek için Sonuç Oluşturucuları kullanma.

Kompozit ankrajlar ilginç bir problem yaratır. Bu çapaları bir ifadede kullanmak, bir dizi kısıtlamayla sonuçlanır. Bu, DSL'nin son kullanıcısı için karışık olabilir.

Bu kısıtlamaları tek tek veya ayrı gruplar halinde değil, standart kod olmadan bir araya getirmenin ve etkin bir şekilde etkinleştirmenin bir yolunu sağlamak istiyoruz.

Swift 5.4'te tanıtılan sonuç oluşturucular (işlev oluşturucular olarak da bilinirler), bir dizi bileşenden dolaylı olarak 'yapı blokları' kullanarak bir sonuç oluşturmanıza olanak tanır. Aslında, Swift UI'nin arkasındaki temel yapı taşlarıdır.

Bu DSL'de nihai sonuç, bir dizi NSLayoutConstraint nesnesidir .

Sonuç oluşturucunun bireysel kısıtlamaları ve kısıtlama dizilerini tek bir dizide çözmesine izin veren derleme işlevleri sağlıyoruz. Tüm bu mantık, DSL'nin son kullanıcısından gizlenmiştir.

Bu işlevlerin çoğunu doğrudan hızlı evrim sonuç oluşturucu teklif örneğinden kopyaladım. Bu DSL'de düzgün çalıştıklarından emin olmak için birim testleri ekledim.

Tüm bunları koymak, aşağıdaki sonuçları verir:

Sonuç oluşturucular ayrıca, bir dizi ile mümkün olmayacak şekilde kapatmaya ek kontrol akışı dahil etmemize izin verir.

İşte bu, okuduğunuz için teşekkürler! Bu makaleyi yazmak günlerce sürdü — bu yüzden yeni bir şey öğrenirseniz bu depoya bir ⭐ eklerseniz sevinirim!

Bana bir tavsiyen varsa çekinme: ve deneyimlerini paylaş!

Bu DSL'nin son sürümü, bir çift AnchorPair türünden yapılmış dört boyutlu bir bağlantı içerir…

Tüm kodu burada bulabilirsiniz: