Создание приложений iOS Swift с применением Microsoft Graph
В этом руководстве рассказывается о создании приложения для iOS с помощью Swift, которое использует API microsoft Graph для получения сведений о календаре для пользователя.
Совет
Если вы предпочитаете просто скачать завершенный учебник, вы можете скачать или клонировать GitHub репозиторий.
Необходимые компоненты
Перед началом этого учебного пособия на компьютере разработки должно быть установлено следующее.
Вы также должны иметь личную учетную запись Майкрософт с почтовым ящиком на Outlook.com или учетную запись Microsoft work или school. Если у вас нет учетной записи Майкрософт, существует несколько вариантов получения бесплатной учетной записи:
- Вы можете зарегистрироваться на новую личную учетную запись Майкрософт.
- Вы можете зарегистрироваться в программе Microsoft 365 разработчика, чтобы получить бесплатную Microsoft 365 подписку.
Примечание
Этот учебник был написан с помощью Xcode версии 12.3 и CocoaPods версии 1.10.1. Действия в этом руководстве могут работать с другими версиями, но это не было проверено.
Отзывы
Обратите внимание на этот учебник в репозитории GitHub.
Создание приложения iOS Swift
Начните с создания нового проекта Swift.
Откройте Xcode. В меню File выберите New, а затем Project.
Выберите шаблон Приложения и выберите Далее.
Установите имя продукта и язык
GraphTutorial
в Swift.Заполните оставшиеся поля и выберите Далее.
Выберите расположение для проекта и выберите Create.
Установка зависимостей
Прежде чем двигаться дальше, установите дополнительные зависимости, которые вы будете использовать позже.
- Библиотека проверки подлинности Майкрософт (MSAL) для iOS для проверки подлинности в Azure AD.
- Microsoft Graph SDK for Objective C для звонков в Microsoft Graph.
- Microsoft Graph модели SDK для objective C для сильно типных объектов, представляющих microsoft Graph ресурсов, таких как пользователи или события.
Выйти из Xcode.
Откройте терминал и измените каталог на расположение вашего проекта GraphTutorial.
Запустите следующую команду, чтобы создать Podfile.
pod init
Откройте Podfile и добавьте следующие строки сразу после
use_frameworks!
строки.pod 'MSAL', '~> 1.1.13' pod 'MSGraphClientSDK', ' ~> 1.0.0' pod 'MSGraphClientModels', '~> 1.3.0'
Сохраните podfile и запустите следующую команду для установки зависимостей.
pod install
После завершения команды откройте вновь созданное пространство GraphTutorial.xcworkspace в Xcode.
Проектирование приложения
В этом разделе будут создаваться представления для приложения: вход на страницу, навигатор панели вкладок, страница приветствия и страница календаря. Вы также создайте наложение индикатора активности.
Создание знака на странице
Расширь папку GraphTutorial в Xcode, а затем выберите ViewController.swift.
В файловом инспекторе измените имя файла на
SignInViewController.swift
.Откройте SignInViewController.swift и замените содержимое следующим кодом.
import UIKit class SignInViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBAction func signIn() { self.performSegue(withIdentifier: "userSignedIn", sender: nil) } }
Откройте main.storyboard. Развяви сцену контроллера представления, выберите контроллер представления.
Выберите инспектор удостоверений, а затем измените выпадание класса на SignInViewController.
Выберите библиотеку, затем перетащите кнопку на контроллер "Вход в поле зрения".
Выбрав выбранную кнопку, выберите инспектор атрибутов и измените название кнопки на
Sign In
.Выбрав выбранную кнопку, выберите кнопку Выравнивание в нижней части сюжетного доски. Выберите горизонтально в контейнере и вертикально в контейнерных ограничениях, оставьте их значения как 0, а затем добавьте 2 ограничения.
Выберите контроллер Вход в представлении, а затем выберите инспектор подключений.
В соответствии с полученными действиями перетащите незаполненным кругом рядом, чтобы подписатьIn на кнопку. Выберите Touch Up Inside в всплывающее меню.
Создание панели вкладок
Выберите библиотеку, затем перетащите контроллер панели вкладок на доску.
Выберите контроллер Вход в представлении, а затем выберите инспектор подключений.
В статье Triggered Segues перетащите неисполненный круг рядом с вручную на контроллер панели вкладок на доске. Выберите Present Modally в всплывающее меню.
Выберите только что добавленную сегу, а затем инспектор атрибутов. Установите поле Идентификатор и установите
userSignedIn
презентацию на полный экран.Выберите элемент 1 Сцена, а затем выберите инспектор подключений.
В статье Triggered Segues перетащите неисполненный круг рядом с вручную на контроллер просмотра sign in the storyboard. Выберите Present Modally в всплывающее меню.
Выберите только что добавленную сегу, а затем инспектор атрибутов. Установите поле Идентификатор и установите
userSignedOut
презентацию на полный экран.
Создание страницы приветствия
Выберите файл Assets.xcassets.
В меню редактора выберите Добавить новый актив, а затем набор изображений.
Выберите новый актив Image и используйте инспектор атрибутов, чтобы установить его имя.
DefaultUserPhoto
Добавьте любое изображение, которое вы хотите выполнять в качестве фотографии профиля пользователя по умолчанию.
Создайте новый файл класса касания какао в папке GraphTutorial с именем
WelcomeViewController
. Выберите UIViewController в подклассе поля.Откройте WelcomeViewController.swift и замените содержимое следующим кодом.
import UIKit class WelcomeViewController: UIViewController { @IBOutlet var userProfilePhoto: UIImageView! @IBOutlet var userDisplayName: UILabel! @IBOutlet var userEmail: UILabel! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // TEMPORARY self.userProfilePhoto.image = UIImage(imageLiteralResourceName: "DefaultUserPhoto") self.userDisplayName.text = "Default User" self.userEmail.text = "default@contoso.com" } @IBAction func signOut() { self.performSegue(withIdentifier: "userSignedOut", sender: nil) } }
Откройте main.storyboard. Выберите сцену Элемента 1, а затем выберите инспектора удостоверений. Измените значение Класса на WelcomeViewController.
С помощью библиотеки добавьте следующие элементы в сцену Item 1.
- Одно представление изображения
- Две метки
- Одна кнопка
С помощью инспектора подключений сделайте следующие подключения.
- Связать выход userDisplayName с первой меткой.
- Ссылка розетки userEmail на вторую метку.
- Привязывай выход userProfilePhoto к представлению изображения.
- Ссылка действия signOut на кнопку Touch Up Inside.
Выберите представление изображения, а затем выберите инспектор размера.
Установите ширину и высоту до 196.
Используйте кнопку Выравнивание, чтобы добавить ограничение горизонтально в контейнере со значением 0.
Используйте кнопку Добавить новые ограничения (рядом с кнопкой Выравнивание), чтобы добавить следующие ограничения:
- Выравнивание сверху до: Сейф области, значение: 0
- Нижнее пространство: имя отображения пользователя, значение: Стандартный
- Высота, значение: 196
- Ширина, значение: 196
Выберите первую метку, а затем используйте кнопку Выравнивание, чтобы добавить ограничение горизонтально в контейнере со значением 0.
Используйте кнопку Добавить новые ограничения, чтобы добавить следующие ограничения:
- Верхнее пространство для: Фотография профиля пользователя, значение: Стандартный
- Нижнее пространство: электронная почта пользователя, значение: Стандартный
Выберите вторую метку, а затем выберите инспектор атрибутов.
Измените цвет на темно-серый и измените шрифт на System 12.0.
Используйте кнопку Выравнивание, чтобы добавить ограничение горизонтально в контейнере со значением 0.
Используйте кнопку Добавить новые ограничения, чтобы добавить следующие ограничения:
- Верхнее пространство: имя отображения пользователя, значение: Стандартный
- Нижнее пространство: Выход, значение: 14
Выберите кнопку, а затем выберите инспектор атрибутов.
Измените название
Sign Out
на .Используйте кнопку Выравнивание, чтобы добавить ограничение горизонтально в контейнере со значением 0.
Используйте кнопку Добавить новые ограничения, чтобы добавить следующие ограничения:
- Верхнее пространство: электронная почта пользователя, значение: 14
Выберите элемент панели вкладок в нижней части сцены, а затем выберите инспектор атрибутов. Измените название
Me
на .
Приветствие должно выглядеть примерно так же, как и после этого.
Создание страницы календаря
Создайте новый файл класса касания какао в папке GraphTutorial с именем
CalendarViewController
. Выберите UIViewController в подклассе поля.Откройте CalendarViewController.swift и замените его содержимое следующим кодом.
import UIKit class CalendarViewController: UIViewController { @IBOutlet var calendarJSON: UITextView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // TEMPORARY calendarJSON.text = "Calendar" calendarJSON.sizeToFit() } }
Откройте main.storyboard. Выберите сцену Элемента 2, а затем выберите инспектора удостоверений. Измените значение Класса на CalendarViewController.
С помощью библиотеки добавьте текстовое представление в элемент 2 Scene.
Выберите только что добавленное текстовое представление. В меню редактора выберите встраить в меню , а затем прокрутите представление.
Разойдите представление прокрутки и текстовое представление, чтобы занять весь экран.
С помощью инспектора подключений соедините выход calendarJSON с текстовым представлением.
Выберите элемент панели вкладок в нижней части сцены, а затем выберите инспектор атрибутов. Измените название
Calendar
на .В меню редактора выберите разрешить проблемы с автостройкой, а затем выберите Добавить отсутствующие ограничения под всеми представлениями в контроллере представления календаря.
После этого сцена календаря должна выглядеть примерно так же.
Создание индикатора активности
Создайте новый файл класса касания какао в папке GraphTutorial с именем
SpinnerViewController
. Выберите UIViewController в подклассе поля.Откройте SpinnerViewController.swift и замените содержимое следующим кодом.
import UIKit class SpinnerViewController: UIViewController { var spinner = UIActivityIndicatorView(style: .large) override func loadView() { view = UIView() view.backgroundColor = UIColor(white: 0, alpha: 0.7) spinner.translatesAutoresizingMaskIntoConstraints = false spinner.startAnimating() view.addSubview(spinner) spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true } public func start(container: UIViewController) { container.addChild(self) self.view.frame = container.view.frame container.view.addSubview(self.view) self.didMove(toParent: container) } public func stop() { self.willMove(toParent: nil) self.view.removeFromSuperview() self.removeFromParent() } }
Тестирование приложения
Сохраните изменения и запустите приложение. Вы должны иметь возможность перемещаться между экранами с помощью кнопок Вход и Выход и вкладка.
Регистрация приложения на портале
В этом упражнении будет создаваться новое родной приложение Azure AD с Azure Active Directory центра администрирования.
Откройте браузер и перейдите в Центр администрирования Azure Active Directory и войдите с помощью личной учетной записи (т.е. учетной записи Майкрософт) или рабочей или учебной учетной записи.
Выберите Azure Active Directory на панели навигации слева, затем выберите Регистрация приложений в разделе Управление.
Выберите Новая регистрация. На странице Зарегистрировать приложение задайте необходимые значения следующим образом.
- Введите имя
iOS Swift Graph Tutorial
. - Введите поддерживаемые типы учетных записей для учетных записей в любом каталоге организаций и личных учетных записей Microsoft.
- Оставьте поле URI перенаправления пустым.
- Введите имя
Нажмите Зарегистрировать. На странице руководство Graph iOS Swift скопируйте значение ID приложения (клиента) и сохраните его, оно потребуется на следующем шаге.
Выберите пункт Проверка подлинности в разделе Управление. Выберите Добавить платформу, затем iOS / macOS.
Введите пакетный ID приложения и выберите Настройка, а затем выберите Готово.
Добавление проверки подлинности с помощью Azure AD
В этом упражнении вы расширит приложение от предыдущего упражнения для поддержки проверки подлинности с помощью Azure AD. Это необходимо для получения необходимого маркера доступа OAuth для вызова microsoft Graph. Для этого в приложение будет интегрирована библиотека проверки подлинности Microsoft (MSAL) для iOS.
Создайте новый файл Списка свойств в проекте GraphTutorial с именем AuthSettings.plist.
Добавьте в файл в словаре Root следующие элементы.
Key Тип Значение AppId
String ID приложения с портала Azure GraphScopes
Массив Три значения строки: User.Read
MailboxSettings.Read
иCalendars.ReadWrite
Важно!
Если вы используете источник управления, например git, то сейчас самое время исключить файл AuthSettings.plist из источника управления, чтобы избежать случайной утечки вашего ID приложения.
Реализация входа в систему
В этом разделе вы настроите проект для MSAL, создайте класс диспетчера проверки подлинности и обновим приложение, чтобы войти и выйти.
Настройка проекта для MSAL
Добавьте новую группу ключей в возможности проекта.
- Выберите проект GraphTutorial, а затем & возможности.
- Выберите + возможность, затем дважды щелкните Keychain Sharing.
- Добавьте группу keychain со значением
com.microsoft.adalcache
.
Управление щелкните Info.plist и выберите Open As, а затем исходный код.
Добавьте в элемент
<dict>
следующее.<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>msauth.$(PRODUCT_BUNDLE_IDENTIFIER)</string> </array> </dict> </array> <key>LSApplicationQueriesSchemes</key> <array> <string>msauthv2</string> <string>msauthv3</string> </array>
Откройте AppDelegate.swift и добавьте следующее утверждение импорта в верхней части файла.
import MSAL
Добавьте к классу
AppDelegate
следующую функцию:func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { guard let sourceApplication = options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String else { return false } return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApplication) }
Создание диспетчера проверки подлинности
Создайте новый файл Swift в проекте GraphTutorial с именем AuthenticationManager.swift. Добавьте указанный ниже код в файл.
import Foundation import MSAL import MSGraphClientSDK // Implement the MSAuthenticationProvider interface so // this class can be used as an auth provider for the Graph SDK class AuthenticationManager: NSObject, MSAuthenticationProvider { // Implement singleton pattern static let instance = AuthenticationManager() private let publicClient: MSALPublicClientApplication? private let appId: String private let graphScopes: Array<String> private override init() { // Get app ID and scopes from AuthSettings.plist let bundle = Bundle.main let authConfigPath = bundle.path(forResource: "AuthSettings", ofType: "plist")! let authConfig = NSDictionary(contentsOfFile: authConfigPath)! self.appId = authConfig["AppId"] as! String self.graphScopes = authConfig["GraphScopes"] as! Array<String> do { // Create the MSAL client try self.publicClient = MSALPublicClientApplication(clientId: self.appId) } catch { print("Error creating MSAL public client: \(error)") self.publicClient = nil } } // Required function for the MSAuthenticationProvider interface func getAccessToken(for authProviderOptions: MSAuthenticationProviderOptions!, andCompletion completion: ((String?, Error?) -> Void)!) { getTokenSilently(completion: completion) } public func getTokenInteractively(parentView: UIViewController, completion: @escaping(_ accessToken: String?, Error?) -> Void) { let webParameters = MSALWebviewParameters(authPresentationViewController: parentView) let interactiveParameters = MSALInteractiveTokenParameters(scopes: self.graphScopes, webviewParameters: webParameters) interactiveParameters.promptType = MSALPromptType.selectAccount // Call acquireToken to open a browser so the user can sign in publicClient?.acquireToken(with: interactiveParameters, completionBlock: { (result: MSALResult?, error: Error?) in guard let tokenResult = result, error == nil else { print("Error getting token interactively: \(String(describing: error))") completion(nil, error) return } print("Got token interactively: \(tokenResult.accessToken)") completion(tokenResult.accessToken, nil) }) } public func getTokenSilently(completion: @escaping(_ accessToken: String?, Error?) -> Void) { // Check if there is an account in the cache var userAccount: MSALAccount? do { userAccount = try publicClient?.allAccounts().first } catch { print("Error getting account: \(error)") } if (userAccount != nil) { // Attempt to get token silently let silentParameters = MSALSilentTokenParameters(scopes: self.graphScopes, account: userAccount!) publicClient?.acquireTokenSilent(with: silentParameters, completionBlock: { (result: MSALResult?, error: Error?) in guard let tokenResult = result, error == nil else { print("Error getting token silently: \(String(describing: error))") completion(nil, error) return } print("Got token silently: \(tokenResult.accessToken)") completion(tokenResult.accessToken, nil) }) } else { print("No account in cache") completion(nil, NSError(domain: "AuthenticationManager", code: MSALError.interactionRequired.rawValue, userInfo: nil)) } } public func signOut() -> Void { do { // Remove all accounts from the cache let accounts = try publicClient?.allAccounts() try accounts!.forEach({ (account: MSALAccount) in try publicClient?.remove(account) }) } catch { print("Sign out error: \(String(describing: error))") } } }
Добавление входных и входных
Откройте SignInViewController.swift и замените содержимое следующим кодом.
import UIKit class SignInViewController: UIViewController { private let spinner = SpinnerViewController() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // See if a user is already signed in spinner.start(container: self) AuthenticationManager.instance.getTokenSilently { (token: String?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let _ = token, error == nil else { // If there is no token or if there's an error, // no user is signed in, so stay here return } // Since we got a token, a user is signed in // Go to welcome page self.performSegue(withIdentifier: "userSignedIn", sender: nil) } } } @IBAction func signIn() { spinner.start(container: self) // Do an interactive sign in AuthenticationManager.instance.getTokenInteractively(parentView: self) { (token: String?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let _ = token, error == nil else { // Show the error and stay on the sign-in page let alert = UIAlertController(title: "Error signing in", message: error.debugDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true) return } // Signed in successfully // Go to welcome page self.performSegue(withIdentifier: "userSignedIn", sender: nil) } } } }
Откройте WelcomeViewController.swift и замените существующую
signOut
функцию на следующую.@IBAction func signOut() { AuthenticationManager.instance.signOut() self.performSegue(withIdentifier: "userSignedOut", sender: nil) }
Сохраните изменения и перезапустите приложение в Simulator.
При входе в приложение необходимо увидеть маркер доступа, отображаемый в окне вывода в Xcode.
Получение сведений о пользователе
В этом разделе вы создадим класс помощников для удержания всех вызовов в Microsoft Graph и обновим его, чтобы использовать этот новый класс для входа в WelcomeViewController
систему пользователя.
Создайте новый файл Swift в проекте GraphTutorial с именем GraphManager.swift. Добавьте указанный ниже код в файл.
import Foundation import MSGraphClientSDK import MSGraphClientModels class GraphManager { // Implement singleton pattern static let instance = GraphManager() private let client: MSHTTPClient? public var userTimeZone: String private init() { client = MSClientFactory.createHTTPClient(with: AuthenticationManager.instance) userTimeZone = "UTC" } public func getMe(completion: @escaping(MSGraphUser?, Error?) -> Void) { // GET /me let select = "$select=displayName,mail,mailboxSettings,userPrincipalName" let meRequest = NSMutableURLRequest(url: URL(string: "\(MSGraphBaseURL)/me?\(select)")!) let meDataTask = MSURLSessionDataTask(request: meRequest, client: self.client, completion: { (data: Data?, response: URLResponse?, graphError: Error?) in guard let meData = data, graphError == nil else { completion(nil, graphError) return } do { // Deserialize response as a user let user = try MSGraphUser(data: meData) completion(user, nil) } catch { completion(nil, error) } }) // Execute the request meDataTask?.execute() } }
Откройте WelcomeViewController.swift и добавьте следующее
import
утверждение в верхней части файла.import MSGraphClientModels
Добавьте в класс
WelcomeViewController
указанное ниже свойство.private let spinner = SpinnerViewController()
Замените
viewDidLoad
существующий следующим кодом.override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.spinner.start(container: self) // Get the signed-in user self.userProfilePhoto.image = UIImage(imageLiteralResourceName: "DefaultUserPhoto") GraphManager.instance.getMe { (user: MSGraphUser?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let currentUser = user, error == nil else { print("Error getting user: \(String(describing: error))") return } // Set display name self.userDisplayName.text = currentUser.displayName ?? "Mysterious Stranger" self.userDisplayName.sizeToFit() // AAD users have email in the mail attribute // Personal accounts have email in the userPrincipalName attribute self.userEmail.text = currentUser.mail ?? currentUser.userPrincipalName ?? "" self.userEmail.sizeToFit() // Save the user's time zone GraphManager.instance.userTimeZone = currentUser.mailboxSettings?.timeZone ?? "UTC" } } }
Если вы сохраните изменения и перезапустите приложение сейчас, после регистрации пользовательский интерфейс обновляется с отображаемого имени пользователя и адресом электронной почты.
Просмотр календаря
В этом упражнении вы будете включать Graph Microsoft в приложение. Для этого приложения для звонков в Корпорацию Майкрософт Graph SDK для objective C для звонков в Microsoft Graph.
Получение событий календаря из Outlook
В этом разделе вы расширит класс, чтобы добавить функцию для получения событий пользователя на текущей неделе и обновить для GraphManager
CalendarViewController
использования этих новых функций.
Откройте GraphManager.swift и добавьте следующий метод в
GraphManager
класс.public func getCalendarView(viewStart: String, viewEnd: String, completion: @escaping(Data?, Error?) -> Void) { // GET /me/calendarview // Set start and end of the view let start = "startDateTime=\(viewStart)" let end = "endDateTime=\(viewEnd)" // Only return these fields in results let select = "$select=subject,organizer,start,end" // Sort results by when they were created, newest first let orderBy = "$orderby=start/dateTime" // Request at most 25 results let top = "$top=25" let eventsRequest = NSMutableURLRequest(url: URL(string: "\(MSGraphBaseURL)/me/calendarview?\(start)&\(end)&\(select)&\(orderBy)&\(top)")!) // Add the Prefer: outlook.timezone header to get start and end times // in user's time zone eventsRequest.addValue("outlook.timezone=\"\(self.userTimeZone)\"", forHTTPHeaderField: "Prefer") let eventsDataTask = MSURLSessionDataTask(request: eventsRequest, client: self.client, completion: { (data: Data?, response: URLResponse?, graphError: Error?) in guard let eventsData = data, graphError == nil else { completion(nil, graphError) return } // TEMPORARY completion(eventsData, nil) }) // Execute the request eventsDataTask?.execute() }
Примечание
Рассмотрим, что делает
getCalendarView
код.- Вызывается URL-адрес
/v1.0/me/calendarview
.- Параметры
startDateTime
endDateTime
и параметры запроса определяют начало и конец представления календаря. - Параметр запроса ограничивает поля, возвращенные для каждого события, только теми, которые будут
select
фактически использовать представление. - Параметр
orderby
запроса сортировать результаты по времени начала. - Параметр
top
запроса запрашивает 25 результатов на страницу. - в загонах Graph майкрософт возвращает время начала и окончания каждого события в часовом
Prefer: outlook.timezone
поясе пользователя.
- Параметры
- Вызывается URL-адрес
Создайте новый файл Swift в проекте GraphTutorial с именем GraphToIana.swift. Добавьте указанный ниже код в файл.
import Foundation // Basic lookup for mapping Windows time zone identifiers to // IANA identifiers // Mappings taken from // https://github.com/unicode-org/cldr/blob/master/common/supplemental/windowsZones.xml class GraphToIana { private static let timeZoneMap = [ "Dateline Standard Time" : "Etc/GMT+12", "UTC-11" : "Etc/GMT+11", "Aleutian Standard Time" : "America/Adak", "Hawaiian Standard Time" : "Pacific/Honolulu", "Marquesas Standard Time" : "Pacific/Marquesas", "Alaskan Standard Time" : "America/Anchorage", "UTC-09" : "Etc/GMT+9", "Pacific Standard Time (Mexico)" : "America/Tijuana", "UTC-08" : "Etc/GMT+8", "Pacific Standard Time" : "America/Los_Angeles", "US Mountain Standard Time" : "America/Phoenix", "Mountain Standard Time (Mexico)" : "America/Chihuahua", "Mountain Standard Time" : "America/Denver", "Central America Standard Time" : "America/Guatemala", "Central Standard Time" : "America/Chicago", "Easter Island Standard Time" : "Pacific/Easter", "Central Standard Time (Mexico)" : "America/Mexico_City", "Canada Central Standard Time" : "America/Regina", "SA Pacific Standard Time" : "America/Bogota", "Eastern Standard Time (Mexico)" : "America/Cancun", "Eastern Standard Time" : "America/New_York", "Haiti Standard Time" : "America/Port-au-Prince", "Cuba Standard Time" : "America/Havana", "US Eastern Standard Time" : "America/Indianapolis", "Turks And Caicos Standard Time" : "America/Grand_Turk", "Paraguay Standard Time" : "America/Asuncion", "Atlantic Standard Time" : "America/Halifax", "Venezuela Standard Time" : "America/Caracas", "Central Brazilian Standard Time" : "America/Cuiaba", "SA Western Standard Time" : "America/La_Paz", "Pacific SA Standard Time" : "America/Santiago", "Newfoundland Standard Time" : "America/St_Johns", "Tocantins Standard Time" : "America/Araguaina", "E. South America Standard Time" : "America/Sao_Paulo", "SA Eastern Standard Time" : "America/Cayenne", "Argentina Standard Time" : "America/Buenos_Aires", "Greenland Standard Time" : "America/Godthab", "Montevideo Standard Time" : "America/Montevideo", "Magallanes Standard Time" : "America/Punta_Arenas", "Saint Pierre Standard Time" : "America/Miquelon", "Bahia Standard Time" : "America/Bahia", "UTC-02" : "Etc/GMT+2", "Azores Standard Time" : "Atlantic/Azores", "Cape Verde Standard Time" : "Atlantic/Cape_Verde", "UTC" : "Etc/GMT", "GMT Standard Time" : "Europe/London", "Greenwich Standard Time" : "Atlantic/Reykjavik", "Sao Tome Standard Time" : "Africa/Sao_Tome", "Morocco Standard Time" : "Africa/Casablanca", "W. Europe Standard Time" : "Europe/Berlin", "Central Europe Standard Time" : "Europe/Budapest", "Romance Standard Time" : "Europe/Paris", "Central European Standard Time" : "Europe/Warsaw", "W. Central Africa Standard Time" : "Africa/Lagos", "Jordan Standard Time" : "Asia/Amman", "GTB Standard Time" : "Europe/Bucharest", "Middle East Standard Time" : "Asia/Beirut", "Egypt Standard Time" : "Africa/Cairo", "E. Europe Standard Time" : "Europe/Chisinau", "Syria Standard Time" : "Asia/Damascus", "West Bank Standard Time" : "Asia/Hebron", "South Africa Standard Time" : "Africa/Johannesburg", "FLE Standard Time" : "Europe/Kiev", "Israel Standard Time" : "Asia/Jerusalem", "Kaliningrad Standard Time" : "Europe/Kaliningrad", "Sudan Standard Time" : "Africa/Khartoum", "Libya Standard Time" : "Africa/Tripoli", "Namibia Standard Time" : "Africa/Windhoek", "Arabic Standard Time" : "Asia/Baghdad", "Turkey Standard Time" : "Europe/Istanbul", "Arab Standard Time" : "Asia/Riyadh", "Belarus Standard Time" : "Europe/Minsk", "Russian Standard Time" : "Europe/Moscow", "E. Africa Standard Time" : "Africa/Nairobi", "Iran Standard Time" : "Asia/Tehran", "Arabian Standard Time" : "Asia/Dubai", "Astrakhan Standard Time" : "Europe/Astrakhan", "Azerbaijan Standard Time" : "Asia/Baku", "Russia Time Zone 3" : "Europe/Samara", "Mauritius Standard Time" : "Indian/Mauritius", "Saratov Standard Time" : "Europe/Saratov", "Georgian Standard Time" : "Asia/Tbilisi", "Volgograd Standard Time" : "Europe/Volgograd", "Caucasus Standard Time" : "Asia/Yerevan", "Afghanistan Standard Time" : "Asia/Kabul", "West Asia Standard Time" : "Asia/Tashkent", "Ekaterinburg Standard Time" : "Asia/Yekaterinburg", "Pakistan Standard Time" : "Asia/Karachi", "Qyzylorda Standard Time" : "Asia/Qyzylorda", "India Standard Time" : "Asia/Calcutta", "Sri Lanka Standard Time" : "Asia/Colombo", "Nepal Standard Time" : "Asia/Katmandu", "Central Asia Standard Time" : "Asia/Almaty", "Bangladesh Standard Time" : "Asia/Dhaka", "Omsk Standard Time" : "Asia/Omsk", "Myanmar Standard Time" : "Asia/Rangoon", "SE Asia Standard Time" : "Asia/Bangkok", "Altai Standard Time" : "Asia/Barnaul", "W. Mongolia Standard Time" : "Asia/Hovd", "North Asia Standard Time" : "Asia/Krasnoyarsk", "N. Central Asia Standard Time" : "Asia/Novosibirsk", "Tomsk Standard Time" : "Asia/Tomsk", "China Standard Time" : "Asia/Shanghai", "North Asia East Standard Time" : "Asia/Irkutsk", "Singapore Standard Time" : "Asia/Singapore", "W. Australia Standard Time" : "Australia/Perth", "Taipei Standard Time" : "Asia/Taipei", "Ulaanbaatar Standard Time" : "Asia/Ulaanbaatar", "Aus Central W. Standard Time" : "Australia/Eucla", "Transbaikal Standard Time" : "Asia/Chita", "Tokyo Standard Time" : "Asia/Tokyo", "North Korea Standard Time" : "Asia/Pyongyang", "Korea Standard Time" : "Asia/Seoul", "Yakutsk Standard Time" : "Asia/Yakutsk", "Cen. Australia Standard Time" : "Australia/Adelaide", "AUS Central Standard Time" : "Australia/Darwin", "E. Australia Standard Time" : "Australia/Brisbane", "AUS Eastern Standard Time" : "Australia/Sydney", "West Pacific Standard Time" : "Pacific/Port_Moresby", "Tasmania Standard Time" : "Australia/Hobart", "Vladivostok Standard Time" : "Asia/Vladivostok", "Lord Howe Standard Time" : "Australia/Lord_Howe", "Bougainville Standard Time" : "Pacific/Bougainville", "Russia Time Zone 10" : "Asia/Srednekolymsk", "Magadan Standard Time" : "Asia/Magadan", "Norfolk Standard Time" : "Pacific/Norfolk", "Sakhalin Standard Time" : "Asia/Sakhalin", "Central Pacific Standard Time" : "Pacific/Guadalcanal", "Russia Time Zone 11" : "Asia/Kamchatka", "New Zealand Standard Time" : "Pacific/Auckland", "UTC+12" : "Etc/GMT-12", "Fiji Standard Time" : "Pacific/Fiji", "Chatham Islands Standard Time" : "Pacific/Chatham", "UTC+13" : "Etc/GMT-13", "Tonga Standard Time" : "Pacific/Tongatapu", "Samoa Standard Time" : "Pacific/Apia", "Line Islands Standard Time" : "Pacific/Kiritimati" ] public static func getIanaIdentifier(graphIdentifer: String) -> String { // If a mapping was not found, assume the value passed // was already an IANA identifier return timeZoneMap[graphIdentifer] ?? graphIdentifer } }
Это простой поиск, чтобы найти идентификатор часовой зоны IANA на основе имени часовой зоны, возвращенного корпорацией Майкрософт Graph.
Откройте CalendarViewController.swift и замените все содержимое следующим кодом.
import UIKit import MSGraphClientModels class CalendarViewController: UIViewController { @IBOutlet var calendarJSON: UITextView! private let spinner = SpinnerViewController() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.spinner.start(container: self) // Calculate the start and end of the current week let timeZone = GraphToIana.getIanaIdentifier(graphIdentifer: GraphManager.instance.userTimeZone) let now = Date() var calendar = Calendar(identifier: .gregorian) calendar.timeZone = TimeZone(identifier: timeZone)! let startOfWeek = calendar.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: now).date! let endOfWeek = calendar.date(byAdding: .day, value: 7, to: startOfWeek)! // Convert start and end to ISO 8601 strings let isoFormatter = ISO8601DateFormatter() let viewStart = isoFormatter.string(from: startOfWeek) let viewEnd = isoFormatter.string(from: endOfWeek) GraphManager.instance.getCalendarView(viewStart: viewStart, viewEnd: viewEnd) { (data: Data?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() // TEMPORARY guard let eventsData = data, error == nil else { self.calendarJSON.text = error.debugDescription return } let jsonString = String(data: eventsData, encoding: .utf8) self.calendarJSON.text = jsonString self.calendarJSON.sizeToFit() } } } }
Теперь вы можете запустить приложение, войти и нажмите элемент навигации Календарь в меню. Вы должны увидеть сброс JSON событий в приложении.
Отображение результатов
Теперь вы можете заменить свалку JSON чем-то, чтобы отобразить результаты в удобной для пользователя манере. В этом разделе вы измените функцию для возврата объектов с сильно типом и измените для отображения событий представление getCalendarView
CalendarViewController
таблицы.
Откройте GraphManager.swift. Замените имеющуюся функцию
getCalendarView
указанным ниже кодом.public func getCalendarView(viewStart: String, viewEnd: String, completion: @escaping([MSGraphEvent]?, Error?) -> Void) { // GET /me/calendarview // Set start and end of the view let start = "startDateTime=\(viewStart)" let end = "endDateTime=\(viewEnd)" // Only return these fields in results let select = "$select=subject,organizer,start,end" // Sort results by when they were created, newest first let orderBy = "$orderby=start/dateTime" // Request at most 25 results let top = "$top=25" let eventsRequest = NSMutableURLRequest(url: URL(string: "\(MSGraphBaseURL)/me/calendarview?\(start)&\(end)&\(select)&\(orderBy)&\(top)")!) // Add the Prefer: outlook.timezone header to get start and end times // in user's time zone eventsRequest.addValue("outlook.timezone=\"\(self.userTimeZone)\"", forHTTPHeaderField: "Prefer") let eventsDataTask = MSURLSessionDataTask(request: eventsRequest, client: self.client, completion: { (data: Data?, response: URLResponse?, graphError: Error?) in guard let eventsData = data, graphError == nil else { completion(nil, graphError) return } do { // Deserialize response as events collection let eventsCollection = try MSCollection(data: eventsData) var eventArray: [MSGraphEvent] = [] eventsCollection.value.forEach({ (rawEvent: Any) in // Convert JSON to a dictionary guard let eventDict = rawEvent as? [String: Any] else { return } // Deserialize event from the dictionary let event = MSGraphEvent(dictionary: eventDict)! eventArray.append(event) }) // Return the array completion(eventArray, nil) } catch { completion(nil, error) } }) // Execute the request eventsDataTask?.execute() }
Создайте новый файл класса касания какао в проекте GraphTutorial с именем
CalendarTableViewController.swift
. Выберите UITableViewController в подклассе поля.Откройте CalendarTableViewController.swift и замените его содержимое следующим.
import UIKit import MSGraphClientModels class CalendarTableViewController: UITableViewController { private let tableCellIdentifier = "EventCell" private var events: [MSGraphEvent]? override func viewDidLoad() { super.viewDidLoad() tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 100 } // Number of sections, always 1 override func numberOfSections(in tableView: UITableView) -> Int { return 1 } // Return the number of events in the table override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return events?.count ?? 0 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: tableCellIdentifier, for: indexPath) as! CalendarTableViewCell // Get the event that corresponds to the row let event = events?[indexPath.row] // Configure the cell cell.subject = event?.subject cell.organizer = event?.organizer?.emailAddress?.name // Build a duration string let duration = "\(self.formatGraphDateTime(dateTime: event?.start)) to \(self.formatGraphDateTime(dateTime: event?.end))" cell.duration = duration return cell } private func formatGraphDateTime(dateTime: MSGraphDateTimeTimeZone?) -> String { guard let graphDateTime = dateTime else { return "" } // Create a formatter to parse Graph's date format let isoFormatter = DateFormatter() isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS" let date = isoFormatter.date(from: graphDateTime.dateTime) // Output like 5/5/2019, 2:00 PM let dateFormatter = DateFormatter() dateFormatter.dateStyle = .short dateFormatter.timeStyle = .short return dateFormatter.string(from: date!) } public func setEvents(events: [MSGraphEvent]?) -> Void { self.events = events self.tableView.reloadData() } }
Создайте новый файл класса касания какао в проекте GraphTutorial с именем
CalendarTableViewCell.swift
. Выберите UITableViewCell в подклассе поля.Откройте CalendarTableViewCell.swift и добавьте в класс следующие
CalendarTableViewCell
свойства.@IBOutlet var subjectLabel: UILabel! @IBOutlet var organizerLabel: UILabel! @IBOutlet var durationLabel: UILabel! var subject: String? { didSet { subjectLabel.text = subject } } var organizer: String? { didSet { organizerLabel.text = organizer } } var duration: String? { didSet { durationLabel.text = duration } }
Откройте main.storyboard и найдите сцену календаря. Удаление представления прокрутки из корневого представления.
С помощью библиотеки добавьте в верхней части представления планку навигации.
Дважды щелкните Название в панели навигации и обнови его до
Calendar
.С помощью библиотеки добавьте элемент кнопки Bar в правую сторону панели навигации.
Выберите новую кнопку панели, а затем выберите инспектор атрибутов. Изменение изображения в плюс.
Добавьте представление контейнера из библиотеки в представление под панели навигации. Resize the container view to take all of the remaining space in the view.
Установите ограничения для панели навигации и представления контейнера следующим образом.
- Панель навигации
- Добавление ограничения: высота, значение: 44
- Добавление ограничения. Ведущее пространство Сейф области, значение: 0
- Добавление ограничения: пространство для Сейф области, значение: 0
- Добавление ограничения: верхнее Сейф области, значение: 0
- Представление контейнера
- Добавление ограничения. Ведущее пространство Сейф области, значение: 0
- Добавление ограничения: пространство для Сейф области, значение: 0
- Добавление ограничения: Верхнее пространство для панели навигации, значение: 0
- Добавление ограничения: нижнее Сейф области, значение: 0
- Панель навигации
Найдите второй контроллер представления, добавленный в сюжетную доску при добавлении представления контейнера. Он подключен к сцене календаря с помощью встраиваемой segue. Выберите этот контроллер и с помощью инспектора удостоверений измените класс на CalendarTableViewController.
Удаление представления из контроллера представления таблицы календаря.
Добавьте представление таблицы из библиотеки в контроллер представления таблицы календаря.
Выберите представление таблицы, а затем выберите инспектор атрибутов. Установите ячейки прототипа до 1.
Перетащите нижний край ячейки прототипа, чтобы предоставить вам большую область для работы.
Используйте библиотеку, чтобы добавить три метки в ячейку прототипа.
Выберите ячейку прототипа, а затем выберите инспектор удостоверений. Изменение класса на CalendarTableViewCell.
Выберите инспектор атрибутов и установите идентификатор
EventCell
.Выбрав EventCell, выберите инспектор подключений и соедините его, а также метки, добавленные в ячейку
durationLabel
наorganizerLabel
subjectLabel
сюжетной доске.Установите свойства и ограничения на трех меток следующим образом.
- Метка subject
- Добавление ограничения: ведущее пространство для просмотра контента, значение: 0
- Добавление ограничения: отставающий пробел для поля для просмотра контента, значение: 0
- Добавление ограничения: верхнее пространство для верхнего поля представления контента, значение: 0
- Метка организатора
- Шрифт: System 12.0
- Добавление ограничения: высота, значение: 15
- Добавление ограничения: ведущее пространство для просмотра контента, значение: 0
- Добавление ограничения: отставающий пробел для поля для просмотра контента, значение: 0
- Добавление ограничения: Верхнее пространство для нижней метки субъекта, значение: Стандартный
- Метка продолжительности
- Шрифт: System 12.0
- Цвет: темно-серый цвет
- Добавление ограничения: высота, значение: 15
- Добавление ограничения: ведущее пространство для просмотра контента, значение: 0
- Добавление ограничения: отставающий пробел для поля для просмотра контента, значение: 0
- Добавление ограничения: Верхнее пространство для основания метки организатора, значение: Стандартный
- Добавление ограничения: нижнее пространство для нижней маржи представления контента, значение: 0
- Метка subject
Выберите EventCell, а затем выберите инспектор размера. Включить автоматическую для высоты строки.
Откройте CalendarViewController.swift и замените его содержимое следующим кодом.
import UIKit import MSGraphClientModels class CalendarViewController: UIViewController { private let spinner = SpinnerViewController() private var tableViewController: CalendarTableViewController? override func viewDidLoad() { super.viewDidLoad() self.spinner.start(container: self) // Calculate the start and end of the current week let timeZone = GraphToIana.getIanaIdentifier(graphIdentifer: GraphManager.instance.userTimeZone) let now = Date() var calendar = Calendar(identifier: .gregorian) calendar.timeZone = TimeZone(identifier: timeZone)! let startOfWeek = calendar.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: now).date! let endOfWeek = calendar.date(byAdding: .day, value: 7, to: startOfWeek)! // Convert start and end to ISO 8601 strings let isoFormatter = ISO8601DateFormatter() let viewStart = isoFormatter.string(from: startOfWeek) let viewEnd = isoFormatter.string(from: endOfWeek) GraphManager.instance.getCalendarView(viewStart: viewStart, viewEnd: viewEnd) { (eventArray: [MSGraphEvent]?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let events = eventArray, error == nil else { // Show the error let alert = UIAlertController(title: "Error getting events", message: error.debugDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true) return } self.tableViewController?.setEvents(events: events) } } } internal override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Save reference to the contained table view if segue.destination is CalendarTableViewController { self.tableViewController = segue.destination as? CalendarTableViewController } } @IBAction func showNewEventForm() { self.performSegue(withIdentifier: "showEventForm", sender: self) } }
Запустите приложение, войди и нажмите вкладку Календарь. Список событий.
Создание нового события
В этом разделе вы добавим возможность создания событий в календаре пользователя.
Откройте GraphManager.swift и добавьте следующую функцию для создания нового события в календаре пользователя.
public func createEvent(subject: String, start: Date, end: Date, attendees: [Substring]?, body: String?, completion: @escaping(MSGraphEvent?, Error?) -> Void) { let isoFormatter = DateFormatter() isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" // Create a dictionary to represent the event // Current version of the Graph SDK models don't serialize properly // see https://github.com/microsoftgraph/msgraph-sdk-objc-models/issues/27 var newEventDict: [String: Any] = [ "subject": subject, "start": [ "dateTime": isoFormatter.string(from: start), "timeZone": self.userTimeZone ], "end": [ "dateTime": isoFormatter.string(from: end), "timeZone": self.userTimeZone ] ] if attendees?.count ?? 0 > 0 { var attendeeArray: [Any] = [] for attendee in attendees! { let attendeeDict: [String: Any] = [ "type": "required", "emailAddress": [ "address": String(attendee) ] ] attendeeArray.append(attendeeDict) } newEventDict["attendees"] = attendeeArray } if !(body?.isEmpty ?? false) { newEventDict["body"] = [ "content": body, "contentType": "text" ] } let eventData = try? JSONSerialization.data(withJSONObject: newEventDict) let createEventRequest = NSMutableURLRequest(url: URL(string: "\(MSGraphBaseURL)/me/events")!) createEventRequest.httpMethod = "POST" createEventRequest.httpBody = eventData createEventRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") let createEventTask = MSURLSessionDataTask(request: createEventRequest, client: self.client, completion: { (data: Data?, response: URLResponse?, graphError: Error?) in guard let eventData = data, graphError == nil else { completion(nil, graphError) return } do { // Deserialize response as event let returnedEvent = try MSGraphEvent(data: eventData) // Return the event completion(returnedEvent, nil) } catch { completion(nil, error) } }) // Execute the task createEventTask?.execute() }
Создайте новый файл класса касания какао в папке GraphTutorial с именем
NewEventViewController
. Выберите UIViewController в подклассе поля.Откройте NewEventViewController.swift и замените его содержимое следующим.
import UIKit import MSGraphClientModels class NewEventViewController: UIViewController { @IBOutlet var subject: UITextField! @IBOutlet var attendees: UITextField! @IBOutlet var start: UIDatePicker! @IBOutlet var end: UIDatePicker! @IBOutlet var body: UITextView! private let spinner = SpinnerViewController() override func viewDidLoad() { super.viewDidLoad() // Add border around text view let borderColor : UIColor = UIColor(red: 0.85, green: 0.85, blue: 0.85, alpha: 1.0) body.layer.borderWidth = 0.5 body.layer.borderColor = borderColor.cgColor body.layer.cornerRadius = 5.0 // Set start picker to the next closest half-hour let now = Date() let calendar = Calendar.current let components = calendar.dateComponents([.minute], from:now) let offset = 30 - (components.minute! % 30) let start = calendar.date(byAdding: .minute, value: offset, to: now) self.start.date = start! // Set end picker to start + 30 min let end = calendar.date(byAdding: .minute, value: 30, to: start!) self.end.date = end! } @IBAction func createEvent() { self.spinner.start(container: self) // Do create let subject = self.subject.text ?? "" let attendees = self.attendees.text?.split(separator: ";") let start = self.start.date let end = self.end.date let body = self.body.text ?? "" GraphManager.instance.createEvent(subject: subject, start: start, end: end, attendees: attendees, body: body) { (event: MSGraphEvent?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let _ = event, error == nil else { // Show the error let alert = UIAlertController(title: "Error creating event", message: error.debugDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true) return } let alert = UIAlertController(title: "Success", message: "Event created", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action: UIAlertAction?) in self.dismiss(animated: true, completion: nil) })) self.present(alert, animated: true) } } } @IBAction func cancel() { self.dismiss(animated: true, completion: nil) } }
Откройте main.storyboard. Используйте библиотеку для перетаскивания контроллера представления на подмок.
С помощью библиотеки добавьте панели навигации в контроллер представления.
Дважды щелкните Название в панели навигации и обнови его до
New Event
.С помощью библиотеки добавьте элемент кнопки Bar в левую сторону панели навигации.
Выберите новую кнопку панели, а затем выберите инспектор атрибутов. Измените название
Cancel
на .С помощью библиотеки добавьте элемент кнопки Bar в правую сторону панели навигации.
Выберите новую кнопку панели, а затем выберите инспектор атрибутов. Измените название
Create
на .Выберите контроллер представления, а затем выберите инспектор удостоверений. Изменение класса на NewEventViewController.
Добавьте в представление следующие элементы управления из библиотеки.
- Добавьте метку под панели навигации. Установите его
Subject
текст. - Добавьте текстовое поле под меткой. Задайте атрибуту Placeholder
Subject
. - Добавьте метку в текстовое поле. Установите его
Attendees
текст. - Добавьте текстовое поле под меткой. Задайте атрибуту Placeholder
Separate multiple entries with ;
. - Добавьте метку в текстовое поле. Установите его
Start
текст. - Добавьте выбор даты под меткой. Установите его предпочтительный стиль для компактности, его интервал до 15 минут, а его высота до 35.
- Добавьте метку под выборщиком дат. Установите его
End
текст. - Добавьте выбор даты под меткой. Установите его предпочтительный стиль для компактности, его интервал до 15 минут, а его высота до 35.
- Добавьте текстовое представление под выборщиком дат.
- Добавьте метку под панели навигации. Установите его
Выберите контроллер представления событий и используйте инспектор подключения, чтобы сделать следующие подключения.
- Подключение отменить полученное действие на кнопку Отмена панели.
- Подключение createEvent получил действие на кнопку Создать планку.
- Подключение субъекта в первое текстовое поле.
- Подключение выход участников на второе текстовое поле.
- Подключение выход на первый выбор даты.
- Подключение конечный выход на второй выборщик даты.
- Подключение на текстовое представление.
Добавьте следующие ограничения.
- Панель навигации
- Leading space to Сейф Area, value: 0
- Отставное пространство Сейф области, значение: 0
- Верхнее Сейф области, значение: 0
- Высота, значение: 44
- Метка subject
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для панели навигации, значение: 20
- Поле Subject Text
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для метки субъекта, значение: Стандартный
- Метка участников
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для темы текстового поля, значение: Стандартный
- Текстовое поле участников
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для метки участников, значение: Стандартный
- Начните метку
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для темы текстового поля, значение: Стандартный
- Выбор даты начала
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для метки участников, значение: Стандартный
- Высота, значение: 35
- End Label
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для даты начала выборки, значение: Стандартный
- Выбор конечных дат
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для конечных меток, значение: Стандартный
- Высота: 35
- Просмотр текста тела
- Ведущее пространство для просмотра маржи, значение: 0
- Отставное пространство для просмотра поля, значение: 0
- Верхнее пространство для выборки конечных дат, значение: Стандартный
- Нижнее пространство для просмотра поля, значение: 0
- Панель навигации
Выберите сцену календаря, а затем выберите инспектор подключений.
В статье Triggered Segues перетащите неисполненный круг рядом с вручную на контроллер представления событий на доске. Выберите Present Modally в всплывающее меню.
Выберите только что добавленную сегу, а затем инспектор атрибутов. Установите поле Идентификатор
showEventForm
.Подключение showNewEventForm получил действие на кнопку + панели навигации.
Сохраните изменения и перезапустите приложение. Перейдите на страницу календаря и нажмите + кнопку. Заполните форму и нажмите кнопку Создать для создания нового события.
Поздравляем!
Вы завершили руководство по Graph Microsoft Graph iOS Swift. Теперь, когда у вас есть рабочее приложение, которое вызывает Microsoft Graph, вы можете экспериментировать и добавлять новые функции. В обзоре microsoft Graph, чтобы увидеть все данные, к ним можно получить доступ с помощью microsoft Graph.
Отзывы
Возникла проблема с этим разделом? Если это так, отправьте нам отзыв, чтобы мы исправили этот раздел.