Sıfırdan Kaydırılabilir UITabBar Nasıl Oluşturulur

Yayınlanan: 2022-03-11

Bildiğiniz gibi, Apple'ın iOS SDK'sı sayısız yerleşik UI bileşeni içerir. Düğmeler, kapsayıcılar, gezinmeler, sekmeli düzenler, adını siz koyun - ihtiyacınız olan hemen hemen her şey orada. Yoksa öyle mi?

Tüm bu temel bileşenler, temel yapılandırılmış UI'ler oluşturmamıza izin verir, ancak kutunun dışına çıkmak gerekirse ne olur; bir iOS geliştiricisinin varsayılan olarak SDK'da desteklenmeyen bir tür davranış oluşturması gerektiğinde?

Bu durumlardan biri, sekmeler arasında kaydırma yeteneğinizin olmadığı ve sekmeler arasında geçiş yapmak için animasyonlarınız olmadığı UITabBar'dır .

Kolay Bir UITabBar Düzeltmesi Arama

Oldukça fazla arama yaptıktan sonra Github'da yalnızca bir tane yararlı kitaplık bulmayı başardım. Ne yazık ki, kütüphane, ilk bakışta zarif bir çözüm gibi görünse de, uygulamayı çalıştırırken birçok sorun yarattı.

Başka bir deyişle, kütüphanenin kullanımını çok kolay buldum, ancak kullanım kolaylığından daha ağır basan ve sorun yaratma eğiliminde olan buggy. Hala ilgileniyorsanız, lib bu bağlantı altında bulunabilir.

Bu yüzden, biraz düşündükten ve çok araştırdıktan sonra kendi çözümümü uygulamaya başladım ve kendi kendime dedim ki: "Hey, kaydırma için sayfa görüntüleme denetleyicisini ve yerel UITabBar'ı kullanırsak ne olur. Ya bu iki şeyi birlikte gruplandırırsak, kaydırırken veya sekme çubuğuna dokunurken sayfa dizinini ele alırsak?

Sonunda, daha sonra açıklayacağım gibi biraz zor olsa da bir çözüm buldum.

Ayrıntılı Bir UITabBar Çözümü

Oluşturulacak üç sekme öğeniz olduğunu düşünün; bu, otomatik olarak her sekme öğesi için görüntülenecek üç sayfanız/kontrolörünüz olduğu anlamına gelir.

Bu durumda, bu üç görünüm denetleyicisini başlatmanız gerekecek ve ayrıca sekme çubuğu öğeleri yapmak, sekmeye basıldığında veya kullanıcı değiştirmek istediğinde durumlarını değiştirmek için sekme çubuğu için iki yer tutucuya/boş görünüm denetleyicisine ihtiyacınız olacak. sekme dizini programlı olarak.

Bunun için, Xcode'a girelim ve sadece bu şeylerin nasıl çalıştığını görmek için birkaç sınıf yazalım.

Sekmeler Arasında Kaydırmaya Bir Örnek

iOS'ta kaydırılabilir sekme örneği

Bu ekran görüntülerinde, ilk sekme çubuğu öğesinin mavi olduğunu, ardından kullanıcının sarı olan sağ sekmeye kaydırdığını ve son ekranda üçüncü öğenin seçildiğini, böylece tüm sayfanın sarı olarak görüntülendiğini görebilirsiniz.

Kaydırılabilir Sekme Çubuğunun Programlı Kullanımı

Öyleyse, bu özelliğe dalalım ve iOS için kaydırılabilir bir sekme çubuğunun kolay bir örneğini yazalım. Öncelikle yeni bir proje oluşturmamız gerekiyor.

Projemiz için gereken ön koşullar oldukça basit: Mac'inizde yüklü Xcode ve Xcode oluşturma araçları.

Yeni bir proje oluşturmak için Mac'inizde Xcode uygulamasını açın ve "Yeni bir Xcode projesi oluştur"u seçin, ardından projenize bir ad verin ve son olarak oluşturulacak uygulama türünü seçin. Basitçe “Single View Uygulaması”nı seçin ve İleri'ye basın.

Xcode Ekran Görüntüsü

Gördüğünüz gibi, bir sonraki ekran bazı temel bilgileri sağlamanızı gerektirecek:

  • Ürün Adı: SwipeableTabbar adını verdim.
  • Ekip: Bu uygulamayı gerçek bir cihazda çalıştırmak istiyorsanız bir geliştirici hesabına sahip olmanız gerekir. Benim durumumda, bunun için kendi hesabımı kullanacağım.

Not: Geliştirici hesabınız yoksa bunu Simulator'da da çalıştırabilirsiniz.

  • Kuruluş Adı: Adını Toptal koydum .
  • Kuruluş Tanımlayıcısı: Adını com.toptal olarak koydum.
  • Dil: Swift'i seçin.
  • "Çekirdek Verileri Kullan", "Birim Testlerini Dahil Et" ve "Kullanıcı Arayüzü Testlerini Dahil Et " seçeneğinin işaretini kaldırın .

İleri düğmesine basın ve kaydırılabilir sekme çubuğunuzu oluşturmaya hazırsınız.

Basit Mimari

Bildiğiniz gibi, yeni bir uygulama oluşturduğunuzda, zaten Main ViewController sınıfına ve Main.Storyboard .

Tasarıma başlamadan önce, işin UI kısmına geçmeden önce her şeyin kurulduğundan ve çalıştığından emin olmak için gerekli tüm sınıfları ve dosyaları oluşturalım.

Projenizin içinde bir yerde birkaç yeni dosya oluşturun -> TabbarController.swift , NavigationController.Swift , PageViewController.swift .

Benim durumumda, buna benziyor.

Ekran Görüntüsü: Xcode Denetleyicileri

Diğer tüm yöntemleri kaldırabileceğiniz için, AppDelegate dosyasında yalnızca didFinishLaunchingWithOptions öğesini bırakın.

didFinishLaunchingWithOptions içinde, aşağıdaki satırları kopyalayıp yapıştırmanız yeterlidir:

 window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = NavigationController(rootViewController: TabbarController()) window?.makeKeyAndVisible() return true

ViewController.Swift adlı dosyadaki her şeyi kaldırın. Bu dosyaya daha sonra döneceğiz.

Öncelikle NavigationController.swift için kod yazalım.

 import Foundation import UIKit class NavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() navigationBar.isTranslucent = true navigationBar.tintColor = .gray } }

Bununla, gri bir TintColor ile yarı saydam bir çubuğumuz olan basit bir UINavigationController oluşturduk. Hepsi bu kadar.

Şimdi, PageViewController almaya devam edebiliriz.

İçinde, tartıştığımız önceki dosyalardan biraz daha fazla kodlamamız gerekiyor.

Bu dosya bir sınıf, bir protokol, bazı UIPageViewController veri kaynakları ve temsilci yöntemleri içerir.

Ortaya çıkan dosyanın şöyle görünmesi gerekiyor:

Xcode Ekran Görüntüsü: Yöntemler

Gördüğünüz gibi, sekme çubuğu denetleyicisine kaydırma işlendikten sonra sayfa dizininin değiştirildiğini söylemesi gereken PageViewControllerDelegate adlı kendi protokolümüzü ilan ettik.

 import Foundation import UIKit protocol PageViewControllerDelegate: class { func pageDidSwipe(to index: Int) }

Ardından, görünüm denetleyicilerimizi tutacak, belirli bir dizindeki sayfaları seçecek ve ayrıca kaydırma işlemlerini gerçekleştirecek PageViewController adlı yeni bir sınıf oluşturmamız gerekiyor.

İlk çalıştırmamızda seçilen ilk denetleyicinin orta görünüm denetleyicisi olması gerektiğini düşünelim. Bu durumda varsayılan indeks değerimizi 1'e eşit olarak atarız.

 class PageViewController: UIPageViewController { weak var swipeDelegate: PageViewControllerDelegate? var pages = [UIViewController]() var prevIndex: Int = 1 override func viewDidLoad() { super.viewDidLoad() self.dataSource = self self.delegate = self } func selectPage(at index: Int) { self.setViewControllers( [self.pages[index]], direction: self.direction(for: index), animated: true, completion: nil ) self.prevIndex = index } private func direction(for index: Int) -> UIPageViewController.NavigationDirection { return index > self.prevIndex ? .forward : .reverse } }

Burada görebileceğiniz gibi, tüm görünüm denetleyicilerimizin referanslarını içerecek olan değişken pages var.

Değişken prevIndex , son seçilen dizini saklamak için kullanılır.

Seçilen dizini ayarlamak için sadece selectPage yöntemini çağırabilirsiniz.

Sayfa dizini değişikliklerini dinlemek istiyorsanız, swipeDelegate abone olmalısınız ve her sayfa kaydırmada, sayfa dizininin değiştiği konusunda bilgilendirileceksiniz, ayrıca mevcut dizini de alacaksınız.

Yöntem yönü, UIPageViewController kaydırma yönünü döndürür. Bu sınıftaki bulmacanın son parçası, temsilci/veri kaynağı uygulamalarıdır.

Neyse ki, bu uygulamalar çok basittir.

 extension PageViewController: UIPageViewControllerDataSource { func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let viewControllerIndex = pages.firstIndex(of: viewController) else { return nil } let previousIndex = viewControllerIndex - 1 guard previousIndex >= 0 else { return nil } guard pages.count > previousIndex else { return nil } return pages[previousIndex] } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let viewControllerIndex = pages.firstIndex(of: viewController) else { return nil } let nextIndex = viewControllerIndex + 1 guard nextIndex < pages.count else { return nil } guard pages.count > nextIndex else { return nil } return pages[nextIndex] } } extension PageViewController: UIPageViewControllerDelegate { func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if completed { guard let currentPageIndex = self.viewControllers?.first?.view.tag else { return } self.prevIndex = currentPageIndex self.swipeDelegate?.pageDidSwipe(to: currentPageIndex) } } }

Yukarıda görebileceğiniz gibi, oyunda üç yöntem vardır:

  • İlki dizini bulur ve önceki görünüm denetleyicisini döndürür.
  • İkincisi dizini bulur ve bir sonraki görünüm denetleyicisini döndürür.
  • Sonuncusu, kaydırmanın bitip bitmediğini kontrol eder, geçerli dizini yerel özellik prevIndex ve ardından ana görünüm denetleyicisine kaydırmanın başarıyla sona erdiğini bildirmek için temsilci yöntemini çağırır.

Artık nihayet UITabBarController uygulamamızı yazabiliriz:

 import UIKit class TabbarController: UITabBarController { let selectedColor = UIColor.blue let deselectedColor = UIColor.gray let tabBarImages = [ UIImage(named: "ic_music")!, UIImage(named: "ic_play")!, UIImage(named: "ic_star")! ] override func viewDidLoad() { view.backgroundColor = .gray self.delegate = self tabBar.isTranslucent = true tabBar.tintColor = deselectedColor tabBar.unselectedItemTintColor = deselectedColor tabBar.barTintColor = UIColor.white.withAlphaComponent(0.92) tabBar.itemSpacing = 10.0 tabBar.itemWidth = 76.0 tabBar.itemPositioning = .centered setUp() self.selectPage(at: 1) } }

Gördüğünüz gibi, varsayılan özellikler ve stille TabbarController oluşturuyoruz. Seçili ve seçili olmayan çubuk öğeleri için iki renk tanımlamamız gerekiyor. Ayrıca, tabbar öğeleri için üç resim ekledim.

viewDidLoad , yalnızca sekme çubuğumuzun varsayılan yapılandırmasını ayarlıyorum ve 1 numaralı sayfayı seçiyorum. Bunun anlamı, başlangıç ​​sayfasının bir numaralı sayfa olacağıdır.

 private func setUp() { guard let centerPageViewController = createCenterPageViewController() else { return } var controllers: [UIViewController] = [] controllers.append(createPlaceholderViewController(forIndex: 0)) controllers.append(centerPageViewController) controllers.append(createPlaceholderViewController(forIndex: 2)) setViewControllers(controllers, animated: false) selectedViewController = centerPageViewController } private func selectPage(at index: Int) { guard let viewController = self.viewControllers?[index] else { return } self.handleTabbarItemChange(viewController: viewController) guard let PageViewController = (self.viewControllers?[1] as? PageViewController) else { return } PageViewController.selectPage(at: index) }

SetUp yönteminin içinde, iki yer tutucu görünüm denetleyicisi oluşturduğumuzu görüyorsunuz. Bu yer tutucu denetleyiciler, UITabBar için gereklidir, çünkü sekme çubuğu öğelerinin sayısı, sahip olduğunuz görünüm denetleyicilerinin sayısına eşit olmalıdır.

Hatırlayabiliyorsanız, denetleyicileri görüntülemek için UIPageViewController kullanıyoruz, ancak UITabBar için, tamamen çalışabilir hale getirmek istiyorsak, tüm görünüm denetleyicilerinin somutlaştırılmasına ihtiyacımız var, böylece çubuk öğeleri onlara dokunduğunuzda çalışacaktır. Dolayısıyla, bu örnekte placeholderviewcontroller #0 ve #2 boş görünüm kontrolörleridir.

Bir ortalanmış görünüm denetleyicisi olarak, üç görünüm denetleyicisine sahip bir PageViewController oluşturuyoruz.

 private func createPlaceholderViewController(forIndex index: Int) -> UIViewController { let emptyViewController = UIViewController() emptyViewController.tabBarItem = tabbarItem(at: index) emptyViewController.view.tag = index return emptyViewController } private func createCenterPageViewController() -> UIPageViewController? { let leftController = ViewController() let centerController = ViewController2() let rightController = ViewController3() leftController.view.tag = 0 centerController.view.tag = 1 rightController.view.tag = 2 leftController.view.backgroundColor = .red centerController.view.backgroundColor = .blue rightController.view.backgroundColor = .yellow let storyBoard = UIStoryboard.init(name: "Main", bundle: nil) guard let pageViewController = storyBoard.instantiateViewController(withIdentifier: "PageViewController") as? PageViewController else { return nil } pageViewController.pages = [leftController, centerController, rightController] pageViewController.tabBarItem = tabbarItem(at: 1) pageViewController.view.tag = 1 pageViewController.swipeDelegate = self return pageViewController } private func tabbarItem(at index: Int) -> UITabBarItem { return UITabBarItem(title: nil, image: self.tabBarImages[index], selectedImage: nil) }

Yukarıda gösterilen birinci ve ikinci yöntem, sayfa görüntüleme denetleyicimizin init yöntemleridir.

Yöntem tabbar öğesi, tabbar öğesini döndürür.

Gördüğünüz gibi createCenterPageViewController() içinde her bir görünüm denetleyicisi için etiketler kullanıyorum. Bu, ekranda hangi denetleyicinin göründüğünü anlamama yardımcı oluyor.

Ardından, muhtemelen en önemli yöntemimiz olan handleTabbarItemChange .

 private func handleTabbarItemChange(viewController: UIViewController) { guard let viewControllers = self.viewControllers else { return } let selectedIndex = viewController.view.tag self.tabBar.tintColor = selectedColor self.tabBar.unselectedItemTintColor = selectedColor for i in 0..<viewControllers.count { let tabbarItem = viewControllers[i].tabBarItem let tabbarImage = self.tabBarImages[i] tabbarItem?.selectedImage = tabbarImage.withRenderingMode(.alwaysTemplate) tabbarItem?.image = tabbarImage.withRenderingMode( i == selectedIndex ? .alwaysOriginal : .alwaysTemplate ) } if selectedIndex == 1 { viewControllers[selectedIndex].tabBarItem.selectedImage = self.tabBarImages[1].withRenderingMode(.alwaysOriginal) } }

Bu yöntemde parametre olarak view controller kullanıyorum. Bu görünüm denetleyicisinden seçili dizin olarak bir etiket alıyorum. Sekme çubuğu için seçili ve seçili olmayan renkleri ayarlamamız gerekiyor.

Şimdi tüm kontrolörlerimiz arasında dolaşmamız ve i == selectedIndex olup olmadığını kontrol etmemiz gerekiyor.

Ardından görüntüyü orijinal render modu olarak oluşturmamız gerekiyor, aksi takdirde görüntüyü şablon modu olarak oluşturmamız gerekiyor.

Şablon modunu kullanarak bir görüntü oluşturduğunuzda, rengi öğenin renk tonu renginden devralır.

Neredeyse bitirdik. Sadece UITabBarControllerDelegate ve PageViewControllerDelegate iki önemli yöntemi tanıtmamız gerekiyor.

 func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { self.selectPage(at: viewController.view.tag) return false } func pageDidSwipe(to index: Int) { guard let viewController = self.viewControllers?[index] else { return } self.handleTabbarItemChange(viewController: viewController) }

İlki, herhangi bir sekme öğesine bastığınızda çağrılır, ikincisi ise sekmeler arasında kaydırdığınızda çağrılır.

Toplama

Tüm kodu bir araya getirdiğinizde, kendi hareket işleyici uygulamanızı yazmanız gerekmediğini ve sekme çubuğu öğeleri arasında sorunsuz kaydırma/kaydırma sağlamak için çok fazla kod yazmanız gerekmediğini fark edeceksiniz.

Burada tartışılan uygulama, tüm senaryolar için ideal olacak bir şey değildir, ancak bu özellikleri küçük bir kodla oluşturmanıza olanak tanıyan ilginç, hızlı ve nispeten kolay bir çözümdür.

Son olarak, yaklaşımımı denemek isterseniz GitHub depomu kullanabilirsiniz. Mutlu kodlama!