Из этого урока по iOS вы узнаете о:
- Различные функции интегрированы в iPadOS.
- Что такое несколько окон на iPad.
- Типы окон.
- Преимущества использования этой функции.
- Какие типы приложений могут извлечь выгоду из этой функции.
- Шаги по интеграции нескольких окон в iPadOS.
iOS 13 была выпущена с множеством новых функций и возможностей. Эти достижения были не только для iPhoneOS, но и для iPadOS. В этом блоге мы поговорим об одной из самых важных функций, которые недавно были запущены — множественные окна. Здесь мы поговорим о том, как интегрировать функцию нескольких окон в iPadOS.
С выпуском iOS13 iPad приблизился к функционированию в качестве основного компьютера. Множество новых функций, выпущенных в iOS13, сделали iPad Pro отличной заменой MacBook. Давайте посмотрим на новые функции, которые сделали этот подвиг.
- Настольный класс Safari.
- Несколько экземпляров приложения (Windows) одновременно.
- Менеджер загрузок Safari.
- Поддержка внешних устройств в файлах.
- Управление локальным хранилищем в файлах.
- Лучшее манипулирование текстом.
- Автоматизация с помощью ярлыков.
- Поддержка мыши.
- Лучший домашний экран.
- Темный режим .
Основная особенность, которая привела к этому продвижению, заключается в использовании нескольких экземпляров (или окон) для одного или нескольких приложений одновременно.
Вам также может понравиться:
Swift Essentials .
Что такое несколько окон на iPad?
Ранее была функция, которая позволяла пользователям иметь несколько вкладок разных приложений на своем iPad, но в iOS 13 было представлено несколько окон для одного и того же или разных приложений.
Эта функция позволяет вашему приложению запускать два экземпляра интерфейса рядом друг с другом. Проще говоря, если это приложение на основе документов, у людей может быть открыто несколько окон документов. Проще говоря, вашим пользователям это понравится.
Бонус : несколько окон легко создаются с помощью простых функций перетаскивания.
Типы окон
- Главное окно : оно содержит несколько объектов приложения и связанные с ними действия. Люди имеют тенденцию взаимодействовать с основным окном с течением времени.
- Вспомогательное окно : оно содержит один объект и действия, связанные с ним. Люди склонны взаимодействовать со вспомогательным окном только один раз, прежде чем закрывать его.
Преимущества нескольких окон
- Несколько окон показывают разные области содержимого. Например, у людей может быть одно основное почтовое окно для отображения их почтового ящика, а другое — для черновика.
- Вспомогательные окна также предоставляют пользователям дополнительные возможности просмотра содержимого и функций приложения.
- Пользователи могут действовать в одном окне и ссылаться на что-то в другом окне.
Какие типы приложений используют эту функцию?
Большинство приложений могут использовать эту функцию так или иначе. Тем не менее, следует убедиться, что эта функция не является обязательной для функционирования вашего приложения. Эта функция предназначена только для улучшения многозадачности iPadOS и повышения удобства работы пользователей.
Некоторые примеры приложений, которые используют эту функцию соответствующим образом:
- Документные приложения.
- Приложения на основе навигации (Карты).
- Приложения для просмотра веб-страниц (Safari).
- Даты / Приложения для управления событиями (Календарь).
Шаги по интеграции нескольких функций Windows в iPadOS
Шаг 1: Создайте новый проект в Xcode
Шаг 2. Создание приложения с одним представлением
Шаг 3: введите имя проекта (например, SOChatDemo)
Шаг 4. Создайте класс «MainSplitViewController» для UISplitViewController
Шаг 5: Добавьте SplitViewController в Main.storyboard и назначьте ему класс MainSplitViewController.
Шаг 6. Перетащите папку «Общие», «Модель» и «Вид» в приложение (из демонстрационного приложения), поскольку оно требуется для данных чата (здесь мы показываем данные чата в автономном режиме)
Шаг 7: Добавьте класс ChatListViewController, чтобы показать список пользователей чата. Получите UIViewController в main.storyboard и назначьте ему класс ChatListViewController.
8:
Возьмите IBOutlet из UITableview и объявите массив списка чата пользователя из класса Model => MessageModel (UserModel)
стриж
1
class ChatListViewController: UIViewController {
2
//MARK:- Variables
3
var arrUserList : [UsersModel] = []
4
//MARK:- IBOutlets
5
@IBOutlet weak var tblUsers: UITableView!
6
//MARK:- UIView Life Cycle
7
override func viewDidLoad() {
8
super.viewDidLoad()
9
arrUserList = generateRandomUsers()
10
tblUsers.estimatedRowHeight = 76
11
tblUsers.rowHeight = UITableView.automaticDimension
12
tblUsers.reloadData()
13
tblUsers.dragDelegate = self
14
15
if arrUserList.count > 0 {
16
DispatchQueue.main.asyncAfter(deadline: .now()+0.2) {
17
self.tblUsers.selectRow(at: IndexPath(row: 0, section: 0),
18
animated: false, scrollPosition: .top)
19
self.setDataInDetailVC(model: self.arrUserList[0])
20
21
}
22
}
23
}
24
}
Шаг 9: Чтобы разделить экран iPad для просмотра двух приложений рядом, следуйте этому коду.
стриж
xxxxxxxxxx
1
//MARK:- UITableViewDragDelegate Delegate
2
extension ChatListViewController: UITableViewDragDelegate {
3
func tableView(_ tableView: UITableView, itemsForBeginning session:
4
UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
5
let selectedMessage = arrUserList[indexPath.row]
6
let userActivity = selectedMessage.openDetailUserActivity
7
let itemProvider = NSItemProvider(object: UIImage(named:
8
selectedMessage.displayImage)!)
9
itemProvider.registerObject(userActivity, visibility: .all)
10
let dragItem = UIDragItem(itemProvider: itemProvider)
12
dragItem.localObject = selectedMessage
13
14
return [dragItem]
15
}
16
}
17
//MARK:- UITableViewDelegate Delegate and UITableViewDataSource
19
extension ChatListViewController : UITableViewDelegate, UITableViewDataSource {
20
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
21
return arrUserList.count
22
}
23
24
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
25
if let cell : ChatUsersTableViewCell =
26
tableView.dequeueReusableCell(withIdentifier:"ChatUsersTableViewCell")
27
as?
28
29
ChatUsersTableViewCell {
30
cell.cellConfig(user: arrUserList[indexPath.row])
31
return cell
32
} else {
33
return UITableViewCell()
34
}
35
}
36
37
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
38
self.setDataInDetailVC(model: arrUserList[indexPath.row])
39
}
40
41
fileprivate func setDataInDetailVC(model:UsersModel) {
42
if let vc : MainSplitViewController = self.navigationController?.parent
43
as? MainSplitViewController {
44
for childern in vc.children {
45
if let navVC = childern as? UINavigationController,let childVC =
46
navVC.viewControllers.first as? ChatViewController {
47
childVC.userModel = model
48
childVC.refreshData()
49
}
50
}
51
}
52
}
53
}
Шаг 10: Добавьте класс ChatViewController, чтобы показать детали чата.
Получите UIViewController в main.storyboard и назначьте ему класс ChatViewController.
Чтобы показать детали чата, мы получим фиктивные сообщения из MessageModel и отобразим их в списке.
стриж
xxxxxxxxxx
1
class ChatViewController: UIViewController {
2
4
//MARK:- Variables
6
var userModel : UsersModel!
8
var isValidFromOtherWindow : Bool = false
10
private var arrMessage : [Messages] = []
12
private var currentUser : UsersModel = UsersModel(senderID: String(0), displayName: generateRandomName(), profession: generateProfessionName(), displayImage: "22")
14
private var activeTextField : UITextView? = nil
16
18
//MARK:- IBOutlets
20
@IBOutlet weak var lblShadowMessage: UILabel!
22
@IBOutlet weak var txtMessage: UITextView!
24
@IBOutlet weak var tblMessages: UITableView!
26
@IBOutlet weak var btnSend: UIButton!
28
@IBOutlet weak var scrollView: UIScrollView!
30
@IBOutlet weak var containerView: UIView!
32
34
//MARK:- UIView Life Cycle
36
override func viewDidLoad() {
38
super.viewDidLoad()
40
setUI()
42
if (isValidFromOtherWindow) {
44
self.navigationItem.hidesBackButton = true
46
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(dismissViewController))
48
50
}
52
NotificationCenter.default.addObserver(
54
self,
56
selector: #selector(self.insertMessages(_:)),
58
name: .messageAddedNotifiation,
60
object: nil)
62
}
64
66
override func viewDidAppear(_ animated: Bool) {
68
super.viewDidAppear(animated)
70
if (isValidFromOtherWindow) {
72
self.refreshData()
74
}
76
self.registerKeyboardNotifications()
78
guard let user = userModel else {
80
return
82
}
84
if #available(iOS 13.0, *) {
86
view.window?.windowScene?.userActivity = user.openDetailUserActivity
88
}
90
92
}
94
96
override func viewWillDisappear(_ animated: Bool) {
98
super.viewWillDisappear(animated)
100
self.deRegisterKeyboardNotifications()
102
if #available(iOS 13.0, *) {
104
view.window?.windowScene?.userActivity = nil
106
}
108
}
110
112
//MARK:- Set UI and Chat Configration
114
func refreshData() {
116
arrMessage = generateRandomMessages(currentUser: currentUser, otherUser: userModel)
118
guard self.tblMessages != nil else {
120
return
122
}
124
self.tblMessages.reloadData()
126
self.tblMessages.scrollToBottom()
128
}
130
132
fileprivate func setUI() {
134
136
txtMessage.layer.cornerRadius = 4
138
if (!isValidFromOtherWindow) {
140
txtMessage.addDoneButtonOnKeyboard()
142
}
144
146
tblMessages.register(UINib(nibName: "IncommingChatMessageTableViewCell", bundle: nil), forCellReuseIdentifier: "IncommingChatMessageTableViewCell")
148
tblMessages.register(UINib(nibName: "OutgoingChatMessageTableViewCell", bundle: nil), forCellReuseIdentifier: "OutgoingChatMessageTableViewCell")
150
tblMessages.estimatedRowHeight = 70
152
tblMessages.rowHeight = UITableView.automaticDimension
154
tblMessages.reloadData()
156
158
}
Шаг 11: Этот метод отвечает за создание оконных сцен и создание нескольких окон.
стриж
xxxxxxxxxx
1
@objc fileprivate func dismissViewController() {
2
if #available(iOS 13.0, *) {
3
var currentSession : UISceneSession? = nil
4
for session in UIApplication.shared.openSessions {
5
if let scene = session.scene,let currentScene = view.window?.windowScene,scene == currentScene {
6
currentSession = session
7
}
8
}
9
guard let session = currentSession else {
10
return
11
}
12
UIApplication.shared.requestSceneSessionDestruction(session, options: nil) { (error) in
13
14
}
15
}
16
17
}
18
19
20
fileprivate func setUpdateLayout() {
21
self.view.updateConstraints()
22
self.view.layoutIfNeeded()
23
self.view.setNeedsLayout()
24
}
25
26
//MARK:- IBAction
27
@IBAction fileprivate func btnSendAction(_ sender: UIButton) {
28
NotificationCenter.default.post(name: .messageAddedNotifiation, object: self.userModel, userInfo: ["data":[txtMessage.text ?? ""]])
29
txtMessage.text = ""
30
lblShadowMessage.text = ""
31
txtMessage.resignFirstResponder()
32
}
33
34
}
35
36
//MARK:- UITableViewDelegate and UITableViewDataSource
37
extension ChatViewController : UITableViewDelegate,UITableViewDataSource {
38
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
39
return arrMessage.count
40
}
41
42
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
43
let message = arrMessage[indexPath.row]
44
if message.sender.senderId == self.currentUser.senderId {
45
if let cell = tableView.dequeueReusableCell(withIdentifier: "OutgoingChatMessageTableViewCell") as? OutgoingChatMessageTableViewCell {
46
cell.cellConfig(model: message)
47
return cell
48
}
49
}else {
50
if let cell = tableView.dequeueReusableCell(withIdentifier: "IncommingChatMessageTableViewCell") as? IncommingChatMessageTableViewCell {
51
cell.cellConfig(model: message)
52
return cell
53
}
54
}
55
56
return UITableViewCell()
57
}
58
59
60
}
61
//MARK:- UITextViewDelegate
63
extension ChatViewController : UITextViewDelegate {
64
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
65
activeTextField = textView
66
return true
67
}
68
func textViewDidBeginEditing(_ textView: UITextView) {
69
70
}
71
72
func textViewDidEndEditing(_ textView: UITextView) {
73
activeTextField = nil
74
}
75
76
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
77
let currentText = textView.text ?? ""
78
guard let stringRange = Range(range, in: currentText) else { return false }
79
let updatedText = currentText.replacingCharacters(in: stringRange, with: text)
80
let previousHeight = lblShadowMessage.frame.height
81
lblShadowMessage.text = updatedText
82
self.tblMessages.layoutIfNeeded()
83
self.setUpdateLayout()
84
let newHeight = lblShadowMessage.frame.height - previousHeight
85
tblMessages.contentOffset = CGPoint(x: 0, y: tblMessages.contentOffset.y + newHeight)
86
self.tblMessages.layoutIfNeeded()
87
return true
88
}
89
}
90
//MARK:- InputBarAccessoryViewDelegate
92
extension ChatViewController {
93
94
@objc fileprivate func insertMessages(_ notification:NSNotification) {
95
guard let user = notification.object as? UsersModel else {
96
return
97
}
98
guard let userModel = self.userModel else {
99
return
100
}
101
if userModel.senderId != user.senderId {
102
return
103
}
104
guard let dictData = notification.userInfo as? [String:Any],let data = dictData["data"] as? [String] else {
105
return
106
}
107
if arrMessage.count == 0 {
108
return
109
}
110
111
for component in data {
112
if component.trimmingCharacters(in: .whitespacesAndNewlines).count == 0 {
113
continue;
114
}else{
115
let message = Messages(sender: currentUser, messageId: String(Int(arrMessage[arrMessage.count-1].messageId) ?? 0 + 1), sentDate: Date(), message: component.trimmingCharacters(in: .whitespacesAndNewlines))
116
arrMessage.append(message)
117
}
118
119
}
120
self.tblMessages.reloadData()
121
self.tblMessages.setNeedsDisplay()
122
self.tblMessages.scrollToBottom()
123
}
124
}
125
//MARK: - Keyboard Notification observer Methods
127
extension ChatViewController {
128
129
fileprivate func registerKeyboardNotifications() {
130
131
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
132
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
134
135
}
137
138
fileprivate func deRegisterKeyboardNotifications() {
139
140
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
141
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidHideNotification, object: nil)
142
}
143
144
@objc fileprivate func keyboardWillShow(notification: NSNotification) {
145
146
if let activeTextField = activeTextField { // this method will get called even if a system generated alert with keyboard appears over the current VC.
147
148
let info: NSDictionary = notification.userInfo! as NSDictionary
149
let value: NSValue = info.value(forKey: UIResponder.keyboardFrameEndUserInfoKey) as! NSValue
150
let keyboardSize: CGSize = value.cgRectValue.size
151
152
let contentInsets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)
153
scrollView.contentInset = contentInsets
154
scrollView.scrollIndicatorInsets = contentInsets
155
156
var aRect: CGRect = self.view.frame
157
aRect.size.height -= keyboardSize.height
158
let activeTextFieldRect: CGRect? = activeTextField.convert(activeTextField.frame, to: self.containerView)//activeTextField.frame
159
160
let activeTextFieldOrigin: CGPoint? = activeTextFieldRect?.origin
161
if (!aRect.contains(activeTextFieldOrigin!)) {
162
scrollView.scrollRectToVisible(activeTextFieldRect!, animated:true)
163
}
164
}
165
}
166
167
168
@objc fileprivate func keyboardWillHide(notification: NSNotification) {
169
170
let contentInsets: UIEdgeInsets = .zero
171
scrollView.contentInset = contentInsets
172
scrollView.scrollIndicatorInsets = contentInsets
173
}
174
}
Заключительные замечания
Apple, возможно, представила эту функцию довольно поздно, но сейчас вам не нужно ничего ждать. Если ваше приложение относится к тому типу, который требует интеграции нескольких окон, то сделайте это. Я надеюсь, что вы нашли этот урок для iPhone полезным, чтобы углубить ваше понимание этой концепции об этой новой функции.