iOS Tutorial: Steps to Integrate Multiple Windows Feature (Introduced in iOS 13) in iPadOS

iOS Tutorial: Steps to Integrate Multiple Windows Feature (Introduced in iOS 13) in iPadOS

Want to integrate multiple windows feature (introduced in iOS 13) in iPadOS? Visit this tutorial and know how to integrate multiple windows feature.

From this iOS tutorial, you will learn:

Different features integrated into iPadOS What multiple windows on the iPad is The types of windows The benefits of using this feature What types of apps can benefit from this feature Steps to integrate multiple windows feature in iPadOS iOS 13 was launched with a lot of new features and functionalities. These advancements were not only for iPhoneOS but also for iPadOS. In this blog, we will talk about one of the most important features that recently launched- multiple windows. Herein we will talk about how to integrate multiple windows feature in iPadOS.

With the release of iOS13, iPad has come nearer to functioning as the main computer. A lot of new features released in iOS13 have made iPad Pro a great MacBook replacement. Let us take a look at the new features that have accomplished this feat.

Desktop Class Safari Multiple App Instances (Windows) At The Same Time Safari Download Manager External Devices Support in Files Local Storage Management in Files Better Text Manipulation Automation with Shortcuts Mouse Support Better Home Screen Dark Mode The main feature that has led to this advancement is using multiple instances (or windows) for single or multiple apps at the same time.

What are the Multiple Windows on the iPad? Previously, there was a feature that lets the users have multiple tabs of different apps on their iPad but multiple windows for the same or different applications were introduced in iOS 13.

This feature enables your app to run two instances of your interface side-by-side. In simple words, if it is a document-based app, people could have multiple document windows open at the same time. In simpler words, your users will love it.

Bonus: Multiple windows are created easily by using simple features of drag and drop.

What are the Types of Windows? Primary window: It contains multiple app objects and the actions associated with them. People tend to interact with a primary window over time. Auxiliary window: It contains a single object and the actions associated with it. People tend to interact with an auxiliary window only once before closing it. What are the Benefits of Multiple Windows? Multiple windows show different areas of the content. For instance, people might have one primary Mail window to display their Inbox and another to show their Drafts mailbox. Auxiliary windows also give users additional views into the app’s content and functionality. Users are enabled to act in one window and refer to something in the other window. Which types of apps utilize this feature? Most apps can utilize this feature in some way or another. Yet, it should be made sure that this feature is not mandatory for the functioning of your app. This feature is only to improve the multitasking feature of iPadOS and enhance user-experience.

Some examples of apps that utilize this feature in an appropriate manner.

Document-based apps Navigation based apps (Maps) Web browsing apps (Safari) Dates/ Event management apps (Calendar) Let us now see the steps of integrating multiple windows feature in iPadOS

Steps to Integrate Multiple Windows Feature in iPadOS

  1. Create a new project in Xcode

integrate multiple windows 2. Create a Single View Application

integrate multiple windows 3. Enter the project name (For instance, SOChatDemo)

integrate multiple windows 4. Create a “MainSplitViewController” class of UISplitViewController

SS4 5. Add and SplitViewController in Main.storyboard and assign MainSplitViewController class to it.

integrate multiple windows integrate multiple windows 6. Drag and drop “Common”, “Model”, and “View” folder in the app (from demo app) because it is required for chat data (here, we are showing offline chat data)

integrate multiple windows 7. Add ChatListViewController class to show the Chat User list.

Get a UIViewController in main.storyboard and assign ChatListViewController class to it.

integrate multiple windows 8 Take IBOutlet of UITableview and declare an array of User’s Chat list from Model => MessageModel class (UserModel)

class ChatListViewController: UIViewController {

//MARK:- Variables var arrUserList : [UsersModel] = []

//MARK:- IBOutlets @IBOutlet weak var tblUsers: UITableView!

//MARK:- UIView Life Cycle override func viewDidLoad() { super.viewDidLoad() arrUserList = generateRandomUsers() tblUsers.estimatedRowHeight = 76 tblUsers.rowHeight = UITableView.automaticDimension tblUsers.reloadData() tblUsers.dragDelegate = self if arrUserList.count > 0 { DispatchQueue.main.asyncAfter(deadline: .now()+0.2) { self.tblUsers.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .top) self.setDataInDetailVC(model: self.arrUserList[0]) } } } } 9. In order to split the iPad screen for viewing two apps side by side, follow this code.

//MARK:- UITableViewDragDelegate Delegate

extension ChatListViewController: UITableViewDragDelegate {

func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {

let selectedMessage = arrUserList[indexPath.row]

let userActivity = selectedMessage.openDetailUserActivity

let itemProvider = NSItemProvider(object: UIImage(named: selectedMessage.displayImage)!)

itemProvider.registerObject(userActivity, visibility: .all)

let dragItem = UIDragItem(itemProvider: itemProvider)

dragItem.localObject = selectedMessage

return [dragItem]

}

}

//MARK:- UITableViewDelegate Delegate and UITableViewDataSource

extension ChatListViewController : UITableViewDelegate, UITableViewDataSource {

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return arrUserList.count

}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

if let cell : ChatUsersTableViewCell = tableView.dequeueReusableCell(withIdentifier: "ChatUsersTableViewCell") as? ChatUsersTableViewCell {

cell.cellConfig(user: arrUserList[indexPath.row])

return cell

}else {

return UITableViewCell()

}

}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

self.setDataInDetailVC(model: arrUserList[indexPath.row])

}

fileprivate func setDataInDetailVC(model:UsersModel) {

if let vc : MainSplitViewController = self.navigationController?.parent as? MainSplitViewController {

for childern in vc.children {

if let navVC = childern as? UINavigationController,let childVC = navVC.viewControllers.first as? ChatViewController {

childVC.userModel = model

childVC.refreshData()

}

}

}

}

} 10. Add ChatViewController class to show Chat Detail.

Get a UIViewController in main.storyboard and assign ChatViewController class to it.

To show the detail of chat we will fetch dummy messages from MessageModel and display in the list.

class ChatViewController: UIViewController {

//MARK:- Variables
var userModel : UsersModel!
var isValidFromOtherWindow : Bool = false
private var arrMessage : [Messages] = []
private var currentUser : UsersModel = UsersModel(senderID: String(0), displayName: generateRandomName(), profession: generateProfessionName(), displayImage: "22")
private var activeTextField : UITextView? = nil

//MARK:- IBOutlets
@IBOutlet weak var lblShadowMessage: UILabel!
@IBOutlet weak var txtMessage: UITextView!
@IBOutlet weak var tblMessages: UITableView!
@IBOutlet weak var btnSend: UIButton!
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var containerView: UIView!

//MARK:- UIView Life Cycle
override func viewDidLoad() {
    super.viewDidLoad()
    setUI()
    if (isValidFromOtherWindow) {
        self.navigationItem.hidesBackButton = true
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(dismissViewController))

    }
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(self.insertMessages(_:)),
        name: .messageAddedNotifiation,
        object: nil)
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    if (isValidFromOtherWindow) {
        self.refreshData()
    }
    self.registerKeyboardNotifications()
    guard let user = userModel else {
        return
    }
    if #available(iOS 13.0, *) {
        view.window?.windowScene?.userActivity = user.openDetailUserActivity
    }

}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    self.deRegisterKeyboardNotifications()
    if #available(iOS 13.0, *) {
        view.window?.windowScene?.userActivity = nil
    }
}

//MARK:- Set UI and Chat Configration
func refreshData() {
    arrMessage = generateRandomMessages(currentUser: currentUser, otherUser: userModel)
    guard self.tblMessages != nil else {
        return
    }
    self.tblMessages.reloadData()
    self.tblMessages.scrollToBottom()
}

fileprivate func setUI() {

    txtMessage.layer.cornerRadius = 4
    if (!isValidFromOtherWindow) {
        txtMessage.addDoneButtonOnKeyboard()
    }

    tblMessages.register(UINib(nibName: "IncommingChatMessageTableViewCell", bundle: nil), forCellReuseIdentifier: "IncommingChatMessageTableViewCell")
    tblMessages.register(UINib(nibName: "OutgoingChatMessageTableViewCell", bundle: nil), forCellReuseIdentifier: "OutgoingChatMessageTableViewCell")
    tblMessages.estimatedRowHeight = 70
    tblMessages.rowHeight = UITableView.automaticDimension
    tblMessages.reloadData()

}
  1. This method is responsible for creating window scenes and creating multiple windows.

@objc fileprivate func dismissViewController() { if #available(iOS 13.0, *) { var currentSession : UISceneSession? = nil for session in UIApplication.shared.openSessions { if let scene = session.scene,let currentScene = view.window?.windowScene,scene == currentScene { currentSession = session } } guard let session = currentSession else { return } UIApplication.shared.requestSceneSessionDestruction(session, options: nil) { (error) in

        }
    }

}


fileprivate func setUpdateLayout() {
    self.view.updateConstraints()
    self.view.layoutIfNeeded()
    self.view.setNeedsLayout()
}

//MARK:- IBAction
@IBAction fileprivate func btnSendAction(_ sender: UIButton) {
    NotificationCenter.default.post(name: .messageAddedNotifiation, object: self.userModel, userInfo: ["data":[txtMessage.text ?? ""]])
    txtMessage.text = ""
    lblShadowMessage.text = ""
    txtMessage.resignFirstResponder()
}

}

//MARK:- UITableViewDelegate and UITableViewDataSource extension ChatViewController : UITableViewDelegate,UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return arrMessage.count }

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let message = arrMessage[indexPath.row]
    if message.sender.senderId == self.currentUser.senderId {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "OutgoingChatMessageTableViewCell") as? OutgoingChatMessageTableViewCell {
            cell.cellConfig(model: message)
            return cell
        }
    }else {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "IncommingChatMessageTableViewCell") as? IncommingChatMessageTableViewCell {
            cell.cellConfig(model: message)
            return cell
        }
    }

    return UITableViewCell()
}

}

//MARK:- UITextViewDelegate extension ChatViewController : UITextViewDelegate { func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { activeTextField = textView return true } func textViewDidBeginEditing(_ textView: UITextView) {

}

func textViewDidEndEditing(_ textView: UITextView) {
    activeTextField = nil
}

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    let currentText = textView.text ?? ""
    guard let stringRange = Range(range, in: currentText) else { return false }
    let updatedText = currentText.replacingCharacters(in: stringRange, with: text)
    let previousHeight = lblShadowMessage.frame.height
    lblShadowMessage.text = updatedText
    self.tblMessages.layoutIfNeeded()
    self.setUpdateLayout()
    let newHeight = lblShadowMessage.frame.height - previousHeight
    tblMessages.contentOffset = CGPoint(x: 0, y: tblMessages.contentOffset.y + newHeight)
    self.tblMessages.layoutIfNeeded()
    return true
}

}

//MARK:- InputBarAccessoryViewDelegate extension ChatViewController {

@objc fileprivate func insertMessages(_ notification:NSNotification) {
    guard let user = notification.object as? UsersModel else {
        return
    }
    guard let userModel = self.userModel else {
        return
    }
    if userModel.senderId != user.senderId {
        return
    }
    guard let dictData = notification.userInfo as? [String:Any],let data = dictData["data"] as? [String] else {
        return
    }
    if arrMessage.count == 0 {
        return
    }

    for component in data {
        if component.trimmingCharacters(in: .whitespacesAndNewlines).count == 0 {
            continue;
        }else{
            let message = Messages(sender: currentUser, messageId: String(Int(arrMessage[arrMessage.count-1].messageId) ?? 0 + 1), sentDate: Date(), message: component.trimmingCharacters(in: .whitespacesAndNewlines))
            arrMessage.append(message)
        }

    }
    self.tblMessages.reloadData()
    self.tblMessages.setNeedsDisplay()
    self.tblMessages.scrollToBottom()
}

}

//MARK: - Keyboard Notification observer Methods extension ChatViewController {

fileprivate func registerKeyboardNotifications() {

    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)


}

fileprivate func deRegisterKeyboardNotifications() {

    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidHideNotification, object: nil)
}

@objc fileprivate func keyboardWillShow(notification: NSNotification) {

    if let activeTextField = activeTextField { // this method will get called even if a system generated alert with keyboard appears over the current VC.

        let info: NSDictionary = notification.userInfo! as NSDictionary
        let value: NSValue = info.value(forKey: UIResponder.keyboardFrameEndUserInfoKey) as! NSValue
        let keyboardSize: CGSize = value.cgRectValue.size

        let contentInsets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)
        scrollView.contentInset = contentInsets
        scrollView.scrollIndicatorInsets = contentInsets

        var aRect: CGRect = self.view.frame
        aRect.size.height -= keyboardSize.height
        let activeTextFieldRect: CGRect? = activeTextField.convert(activeTextField.frame, to: self.containerView)//activeTextField.frame

        let activeTextFieldOrigin: CGPoint? = activeTextFieldRect?.origin
        if (!aRect.contains(activeTextFieldOrigin!)) {
            scrollView.scrollRectToVisible(activeTextFieldRect!, animated:true)
        }
    }
}


@objc fileprivate func keyboardWillHide(notification: NSNotification) {

    let contentInsets: UIEdgeInsets = .zero
    scrollView.contentInset = contentInsets
    scrollView.scrollIndicatorInsets = contentInsets
}

} Concluding Remarks Apple might have introduced this feature quite late but you should not wait for anything now. If your app is the type that requires integrating multiple windows feature, then go for it. We hope that you found this iPhone tutorial useful in clearing your concept about this new feature.

In case, if you have any suggestions or queries in this tutorial or any questions related to iPhone app development, we are all ears. You may also feel free to tell us what we have missed in this or if you feel something is unclear.

We are a leading iPhone app development company and have hands-on experience in developing iOS apps with top features and functionalities. If you wish to develop a performance-oriented app with advanced features like this or want to hire iPhone developers, contact us. You may schedule a 30-min free consultation with our iOS developers and expert. All you need to do is fill the contact us form in the footer.

You may also like:

How to Integrate the New ‘Sign in with Apple’ Button in Your iOS App?

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

Brave, Chrome, Firefox, Opera or Edge: Which is Better and Faster?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Top iOS Mobile App Development Agency in New York

Are you looking for a [Top iOS Mobile App Development Agency in New York]**(https://www.appcluesinfotech.com/ios-app-develop**ment-company/ "Top iOS Mobile App Development Agency in New York") for your app projects? Then AppClues Infotech is the...

Top iOS Mobile App Development Companies in the USA & India

Hire Top iOS app development companies in the USA and India. You can find the ultimate list of the best iOS app development companies in USA and India.

Top 10 Essential Tips For iOS App Development

More and more companies are taking up a mobile-first approach to make lives productive for both enterprises and consumers. iOS app developers who can build apps for a wide range of Apple devices are the best fit for the companies looking to...