UIKit ile PopOver Kullanımı

Sercan Kaya
Mobillium
Published in
5 min readApr 12, 2022

Merhaba Arkadaşlar, Sizlere bugün iOS’ta PopOver Button kullanımından bahsedeceğim umarım okurken keyif alırsınız.

Öncelikle PopOver olarak göstereceğimiz bir ekran tasarlamamız gerekiyor. Ben buraya programatically olarak kodlama yapacağım.

private let popOverbutton: UIButton = {let button = UIButton()button.backgroundColor = .bluebutton.tintColor = .whitebutton.setTitleColor(.white, for: .normal)button.setTitle(“PopOverButton”, for: .normal)button.addTarget(self, action: #selector(popOverButtonTapped), for: .touchUpInside)

Yukarıda göründüğü gibi tıklandığı zaman PopOver açacak bir buton oluşturdum. Daha sonra bu butonu ekranda konumlandıracağım. Altta göründüğü gibi ekranda konumlandırma işlemini yaptım.

view.addSubview(popOverbutton)NSLayoutConstraint.activate([popOverbutton.centerYAnchor.constraint(equalTo: view.centerYAnchor),popOverbutton.centerXAnchor.constraint(equalTo: view.centerXAnchor),popOverbutton.widthAnchor.constraint(equalToConstant: 200),popOverbutton.heightAnchor.constraint(equalToConstant: 50)])

Şimdi ise PopOver olayını başka ekranlarda kullanabileceğimizi düşünerek bir helper dosyası açacağım. PopOverHelper.swift adında bir dosya oluşturup singleton olarak PopOver fonksiyonumu buradan çağıracağım. Aslında bir navigate işlemi yapacağız ama bunu PopOver şeklinde yapacağız

final class PopOverHelper {static var shared = PopOverHelper()func showMenu(inViewController: PresentedPopOverViewController,sender: UIButton, arrowDirection: UIPopoverArrowDirection = .up, delegate: PopOverMenuViewControllerDelegate) {// present işleminin stilini belirtiyoruz.viewController.modalPresentationStyle = .popover// Açılacak sayfa için bir size belirtiyoruzviewController.preferredContentSize = .init(width: 240, height: 135)let presentationController = viewController.popoverPresentationController// delegate için fonksiyon da bir viewController parametresi alıyoruz.presentationController?.delegate = inViewController// OK İşareti hangi yönde açılacak onu belirtiyoruz.// Farklı sayfalarda kullanacağım için onuda parametre olarak alıp default bir tip veriyorumpresentationController?.permittedArrowDirections = arrowDirection// PopOver yerini bulmak için bizden bir kaynak view istemektedir. butonun sender parametresini buraya gönderiyoruz.presentationController?.sourceView = senderpresentationController?.sourceRect = sender.bounds}}

Okların Açılış Yönlerini alt resimlerde görebilirsiniz.

presentationController?.permittedArrowDirections = .down
presentationController?.permittedArrowDirections = .right
presentationController?.permittedArrowDirections = .left
presentationController?.permittedArrowDirections = .up

En son resimde görüldüğü üzere burada bir kayma söz konusu. Resmin açıklamasında ilgili kodu görebilirsiniz.

tableView.contentInsetAdjustmentBehavior = .always

Şimdi artık ekranda görmüş olduğunuz kısmı tasarlama işlemine geçebiliriz. Bir PopOver ekran tasarımı yapacağız.

PopOverMenuViewController dosyası oluşturup içerisinde tableView eklemesi yapacağız.

final class PopOverMenuViewController: UIViewController {private let tableView: UITableView = {let tableView = UITableView()tableView.register(PopOverMenuTableViewCell.self, forCellReuseIdentifier: PopOverMenuTableViewCell.cellid)tableView.translatesAutoresizingMaskIntoConstraints = falsetableView.isScrollEnabled = falsetableView.backgroundColor = .cleartableView.contentInsetAdjustmentBehavior = .alwaystableView.showsVerticalScrollIndicator = falsereturn tableView}()override func viewDidLoad() {super.viewDidLoad()addSubViews()tableView.delegate = selftableView.dataSource = self}}

TableView değişkenimizi tanımladık, artık ekrana konumlandırma işlemini yapabiliriz.

// MARK: — UI Layoutextension PopOverMenuViewController {private func addSubViews() {addTableView()}private func addTableView() {view.addSubview(tableView)NSLayoutConstraint.activate([tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),tableView.topAnchor.constraint(equalTo: view.topAnchor),tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),])}}

Ekrana konumlandırma işlemi de bittikten sonra cell oluşturma işlemine başlayabiliriz.

class PopOverMenuTableViewCell: UITableViewCell {static let cellid = “PopOverMenuCell”private let buttonImage: UIImageView = {let imageView = UIImageView()imageView.tintColor = .blackimageView.translatesAutoresizingMaskIntoConstraints = falsereturn imageView}()private let titleLabel: UILabel = {let label = UILabel()label.textColor = .blacklabel.translatesAutoresizingMaskIntoConstraints = falsereturn label}()override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {super.init(style: style, reuseIdentifier: reuseIdentifier)selectionStyle = .noneaddSubViews()}required init?(coder: NSCoder) {fatalError(“init(coder:) has not been implemented”)}public func set(title: String, image: UIImage?) {buttonImage.image = imagetitleLabel.text = title}}// MARK: — UILayoutextension PopOverMenuTableViewCell {private func addSubViews() {addImageView()addTitleLabel()}private func addImageView() {contentView.addSubview(buttonImage)NSLayoutConstraint.activate([buttonImage.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),buttonImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),buttonImage.widthAnchor.constraint(equalToConstant: 16),buttonImage.heightAnchor.constraint(equalToConstant: 16)])}private func addTitleLabel() {contentView.addSubview(titleLabel)NSLayoutConstraint.activate([titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor),titleLabel.leadingAnchor.constraint(equalTo: buttonImage.trailingAnchor, constant: 16),titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),])}}

Cell oluşturma işlemi bittikten sonra artık bu cell’i tabloda gösterebiliriz.

extension PopOverMenuViewController: UITableViewDelegate {}extension PopOverMenuViewController: UITableViewDataSource {func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return cellItem.count}func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {return 45}func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let cell: PopOverMenuTableViewCell = tableView.dequeueReusableCell(withIdentifier: PopOverMenuTableViewCell.cellid, for: indexPath) as! PopOverMenuTableViewCelllet item = cellItem[indexPath.row]cell.set(title: item.title, image: item.image)return cell}}

Tıkladığım butonun hangi buton olduğunu anlamak için ve butonların resim ve isimlerini bir modelden almak istediğim için bir model ve enum oluşturuyorum.

public enum PopOverMenuType {case delete, share, edit}struct PopOverMenuModel {init(title: String, image: UIImage?, type: PopOverMenuType) {self.title = titleself.image = imageself.type = type}let title: Stringlet image: UIImage?let type: PopOverMenuType}

Şimdi bir dizi oluşturup bu diziye tabloda göstereceğim.

let cellItem: [PopOverMenuModel] = [PopOverMenuModel(title: “Edit”, image: UIImage(systemName: “pencil”), type: .edit), PopOverMenuModel(title: “Delete”, image: UIImage(systemName: “trash”), type: .delete),PopOverMenuModel(title: “Share”, image: UIImage(systemName: “arrowshape.turn.up.right.fill”), type: .share)]

Artık bu tabloda tıkladığım butonun tipini bir PopOver’ı açtığım sayfaya aktarmak istiyorum. bunun için de bir delegate yazacağım ve bunu didSelect fonksiyonunda çağıracağım.

public protocol PopOverMenuViewControllerDelegate: AnyObject {func didSelectDelegate(type: PopOverMenuType)}final class PopOverMenuViewController: UIViewController {weak var delegate: PopOverMenuViewControllerDelegate?}func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {dismiss(animated: false, completion: nil)delegate?.didSelectDelegate(type: cellItem[indexPath.row].type)}

Artık işlemler bitti sayılır, ufak bir adım daha kaldı. Yukarıda yazdığımız delegate’i tetiklemek için ana ekranda çağırıp, anaekran.delegate = self dememiz gerekiyor. Bu sebeble showMenu fonksiyonuna ilgili delegate’i parametre olarak istiyorum ve burada viewController içerisindeki delegate ile fonksiyondan aldığım delegate parametresini eşitliyorum.

func showMenu(inViewController: PresentedPopOverViewController,sender: UIButton, arrowDirection: UIPopoverArrowDirection = .up, delegate: PopOverMenuViewControllerDelegate) {let viewController = PopOverMenuViewController()viewController.delegate = delegate}

Arık PopOver menümüz hazır, bir action yazıp PopOverHelper kullanarak menüyü açabiliriz.

// MARK: — Actionsextension ViewController {@objcfunc popOverButtonTapped(_ sender: UIButton) {PopOverHelper.shared.showMenu(inViewController: self, sender: sender, arrowDirection: .any, delegate: self)}}

Ve son adım menüde tıkladığımız butonun tipine aşağıdaki delegate fonksiyonu ile ulaşabilir, gerekli işlemleri yapabiliriz.

extension ViewController: UIPopoverPresentationControllerDelegate, PopOverMenuViewControllerDelegate {func didSelectDelegate(type: PopOverMenuType) {switch type {case .delete:print(“delete”)case .share:print(“share”)case .edit:print(“edit”)}}

Umarım okurken keyif almışsınızdır ve faydalı olmuştur.

Proje reposunu aşağıya bırakıyorum.

Keyifli kod yazmalar…

--

--