swift iMessage style keyboard input - swift

i am trying to implement an iMessage style keyboard input with a textview that sits at the bottom of the screen then slides up with the keyboard when you touch the textView, then is docked to the top of the keyboard.
I found MessageComposerView which is exactly what I want. Unfortunately I cannot get it working ( I am using swift).
below is my code:
import UIKit
class CommentsViewController: UIViewController, MessageComposerViewDelegate {
var messageComposerView: MessageComposerView!
override func viewDidLoad() {
super.viewDidLoad()
let defaultWidth = view.frame.size.width
let defaultHeight = CGFloat(54.0)
let subviewFrame = CGRect(x: 0, y: view.frame.height - defaultHeight, width: defaultWidth, height: defaultHeight)
messageComposerView = MessageComposerView(frame: subviewFrame) as MessageComposerView
view.addSubview(messageComposerView)
}
func messageComposerSendMessageClickedWithMessage(message: String!) {
}
}
however it does not show up. Ive printed the view and its frame is correct, its just that there is nothing there for some reason. Everything looks like it should be working. Does anyone see anything wrong with my current implementation?

Try PHFComposeBarView Library (https://github.com/fphilipe/PHFComposeBarView), it's a exact copy of the iMessage composer bar that can be used in C & Swift from storyboard or code
Code Example : https://github.com/liveminds/SwiftPHFComposeBarTest
Storyboard Example : https://github.com/liveminds/SwiftPHFComposeBarTest/tree/storyboard-managed
To add the bar to your view:
drag a new UIView on your UIViewcontroller, assign "PHFComposeBarView" class to UIView
Add an outlet of the UIView to your Viewcontroller's class
add "PHFComposeBarViewDelegate" to your Viewcontroller's class
assign UIView Delegate in viewdidload() : self.composerBarOutlet.delegate = self
Set the composer bar to appear as inputAccessoryView above the keyboard:
override var inputAccessoryView: UIView {
composerBar.removeFromSuperview()
return composerBar
}
Style your Bar(example):
composerBar.utilityButtonImage = UIImage(named: "fullStar")!
composerBar.buttonTitle = "Submit"
composerBar.maxCharCount = 200
composerBar.maxLinesCount = 5
composerBar.alpha = 1.0
composerBar.buttonTintColor = AppConfig.BLUECOLOR
composerBar.placeholder = "What do you think about this product?"

Try following this example by Andrew Bancroft Send Text Message In-App – Using MFMessageComposeViewController with Swift. His example provides a nice walkthrough using Swift and includes sample code on GitHub.
Just going from the code snippet included in your post, it looks like you need to import Foundation, import MessageUI, conform to the MFMessageComposeViewControllerDelegate protocol, and implement the messageComposeViewController protocol method. All of this is covered in Andrew's blog post. This should give you what you're looking for.

Have you find the answer yet?
I use the same Framework however I believe you need to set up the delegate to self. IN my case it fails though I dont know why but it is written down in the readme github.

Related

Swift macOS SegmentedControl Action not getting called

Description
I am trying to use NSSegmentedControls to transition between Child ViewControllers. The ParentViewController is located in Main.storyboard and the ChildViewControllers are located in Assistant.storyboard. Each ChildViewController has a SegmentedControl divided into 2 Segments and their primary use is to navigate between the ChildViewControllers. So they are set up as momentaryPushIn rather than selectOne. Each ChildViewController uses a Delegate to communicate with the ParentViewController.
So in the ParentViewController I added the ChildViewControllers as following:
/// The View of the ParentViewController configured as NSVisualEffectView
#IBOutlet var visualEffectView: NSVisualEffectView!
var assistantChilds: [NSViewController] {
get { return [NSViewController]() }
set(newValue) {
for child in newValue { self.addChild(child) }
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
addAssistantViewControllersToChildrenArray()
}
override func viewWillAppear() {
visualEffectView.addSubview(self.children[0].view)
self.children[0].view.frame = self.view.bounds
}
private func addAssistantViewControllersToChildrenArray() -> Void {
let storyboard = NSStoryboard.init(name: "Assistant", bundle: nil)
let exampleChild = storyboard.instantiateController(withIdentifier: "ExampleChild") as! ExampleChildViewController
let exampleSibling = storyboard.instantiateController(withIdentifier: "ExampleSibling") as! ExampleSiblingViewController
exampleChild.navigationDelegate = self
exampleSibling.navigationDelegate = self
assistantChilds = [exampleChild, exampleSibling]
}
So far so good. The ExampleChildViewController has an NSTextField instance. While I am in the scope of the TextField, I can trigger the action of the SegmentedControls. Its navigating forward and backward as it should. But once I leave the scope of the TextField I can still click the Segments, but they are not triggering any action. They should be able to navigate forward and backward even if the TextField is not the current "First Responder" of the application. I think I am missing something out here, I hope anyone can help me with this. I know the problem is not the NSSegmentedControl because I am seeing the same behavior with an NSButton, which is configured as Switch/Checkbox, in the SiblingViewController. I just don't have any idea anymore what I am doing wrong.
It`s my first time asking a question myself here, so I hope the way I am doing is fine for making progress with the solution. Let me know if I can do something better/different or if I need to provide more information about something.
Thanks in advance!
Additional Information
For the sake of completeness:
The ParentViewController itself is embedded in a ContainerView,
which is owned by the RootViewController. I can't imagine this does
matter in any way, but this way we are not missing something out.
I am actually not showing the navigation action, because I want to
keep it as simple as possible. Furthermore the action is not problem,
it does what I want it to do. Correct me if I am wrong with this.
Possible solutions I found while researching, which did not work for me:
Setting window.delegate of the ChildViewControllers to NSApp.windows.first?.delegate
Setting the ChildViewController to becomeFirstResponder in its func viewWillAppear()
visualEffectView.addSubview(self.children[0].view, positioned: NSWindow.OrderingMode.above, relativeTo: nil)
Related problems/topics I found while researching:
Basic segmented control not working
Adding and Removing Child View Controllers
NSSegmentedControl - Odd appearance when placed in blur view
How to set first responder to NSTextView in Swift?
How to use #selector in Swift 2.2 for the first responder
Accessing methods, actions and/or outlets from other controllers with swift
How to use Child View Controllers in Swift 4.0 programmatically
Container View Controllers
issues with container view
Control a NSTabViewController from parent View
How to detect when NSTextField has the focus or is it`s content selected cocoa
SOLUTION
let parentViewControllerInstance = self.parent as! ParentViewController
segmentedControl.target = parentViewControllerInstance
In my case I just had to set the delegate as the target of the sendAction method.
Background
Ok, after hours of reading the AppKit Documentation I am now able to answer my own question.
First, debugging the UI showed that the problem was definitely not in the ViewHierarchy.
So I tried to think about the nature of NSButton and NSSegmentedControl. At some point I noticed that both are subclasses of NSControl.
class NSSegmentedControl : NSControl
class NSButton : NSControl
The AppKit Documentation says:
Discussion
Buttons are a standard control used to initiate actions within your app. You can configure buttons with many different visual styles, but the behavior is the same. When clicked, a button calls the action method of its associated target object. (...) You use the action method to perform your app-specific tasks.
The bold text points to the key of the solution – of its associated target object. Typically I define the action of an control item like this:
button.action = #selector(someFunc(_:))
This causes the NSControl instance to call this:
func sendAction(_ action: Selector?, to target: Any?) -> Bool
Parameter Description from the documentation:
Parameters
theAction
The selector to invoke on the target. If the selector is NULL, no message is sent.
theTarget
The target object to receive the message. If the object is nil, the application searches the responder chain for an object capable of handling the message. For more information on dispatching actions, see the class description for NSActionCell.
In conclusion the NSControl instance, which was firing the action method (in my case the NSSegmentedControl), had no target to send its action to. So it was only able to send its action method across the responder chain - which obviously has been nil while the first responder was located in another view.

Cannot modify NSTabViewItem

I may be getting lost in a glass of water as I am not an experienced developer but I cannot seem to be able to implement a simple override to modify the size of an NSTabView item.
I have a Tab View Controller (Style = toolbar)
I have a Tabless Tab View
I have 3 Tab Items. For testing I have only subclassed one of them to the subclass below
I have created a new subclass of NSTabViewItem: MyTabViewItem and subclassed one of the 3 tab Items. The code is:
import Cocoa
class MyTabViewItem: NSTabViewItem {
override func drawLabel(_ shouldTruncateLabel: Bool, in labelRect: NSRect) {
var size = self.sizeOfLabel(false)
size.width = 180
print("Draw!!")
}
override func sizeOfLabel(_ computeMin: Bool) -> NSSize {
var size = super.sizeOfLabel(false)
size.width = 180
print("Draw!!")
return size
}
}
Everything works, except the subclassing. The Tabs appear, they do operate by switching the views and the program runs as it should. Except that it does not resize the Tab Item. The code in the subclass MyTabViewItem is never reached (it never prints Draw!! as it should.
I cannot understand what I am missing here. I have not read of any IB connection to make (and I cannot seem to be able to connect the Tab Items anyways). Please apologise if it isa trivial question but I have searched everywhere and not found anything to help me.
Thank you
You said:
I have a Tabless Tab View
This is your problem. An NSTabView only asks an NSTabViewItem to drawLabel if the NSTabView itself is responsible for drawing the tab bar, but you have a “Tabless” tab view. (“Tabless” is the default style when you drag an NSTabViewController into a storyboard.)
You also said:
I have a Tab View Controller (Style = toolbar)
So you don't even want the tab view to draw a tab bar; you want items in the window toolbar to select tabs (like in Xcode's preference window).
Your ability to customize the toolbar items created for your tabs is limited. You can subclass NSTabViewController and override toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:, like this:
override func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
let toolbarItem = super.toolbar(toolbar, itemForItemIdentifier: itemIdentifier, willBeInsertedIntoToolbar: flag)
if
let toolbarItem = toolbarItem,
let tabViewItem = tabViewItems.first(where: { ($0.identifier as? String) == itemIdentifier.rawValue })
{
toolbarItem.label = "\(tabViewItem.label) 😀"
}
return toolbarItem
}
But I found that making other changes didn't work well:
Setting toolbarItem.image didn't work well for me.
Setting toolbarItem.view made the item stop receiving clicks.
Note that the minSize and maxSize properties are only used if toolbarItem.view is set.
Your best bet is probably to manage the toolbar yourself, without trying to use NSTabViewController's support.
I have also subclassed the NSTabViewController as follows:
import Cocoa
class MyTabViewController: NSTabViewController {
#IBOutlet weak var TradingTabItem: MyTabViewItem!
override func viewDidLoad() {
super.viewDidLoad()
print("Loaded Tab View")
TradingTabItem.label = "New"
// Do view setup here.
}
}
What happens now is that the tab item in my subclass (the only one of the 3 I subclassed) does change its label string to New. However, even if I have added the item as an IBOutlet here, it still does not change seize (and the overridden sizeOfLabel function is not reached).

Swift UITableView inside Tabbar

I'm new in Swift, and now I'm trying to develop an app. I need to show UITabBarController with 3 tabs. Each tab has the same ViewController (it is custom class, inherited from UIViewController).
Sequence of my actions:
1. Put TabBarController in storyboard, dropped Tab1 and Tab2 which were situateed in it by default, and pointed a custom class (ArchiveTabBarController) and Storyboard ID (ArchiveTabBarVC).
2. Put UITableViewController in storyboard, pointed custom class (ArchiveTableViewController), and pointed storyboard ID (archiveTableVC).
3. Made a custom cell in this TableViewController: pointed a custom class (ArchiveTableViewCell), and pointed identifier (archiveTableViewCell).
4. In my ArchiveTabBarController, I wrote such code:
override func viewWillAppear(_ animated : Bool) {
super.viewWillAppear(animated);
let item1 = ArchiveTableViewController();
item1.archiveType = "";
let item2 = ArchiveTableViewController();
item2.archiveType = "type1";
let item3 = ArchiveTableViewController();
item3.archiveType = "type2";
let controllers = [item1, item2, item3];
self.viewControllers = controllers;
}
override func viewDidLoad() {
super.viewDidLoad();
delegate = self;
}
In my ArchiveTableViewController, in viewDidLoad(), I make GET-Request to load data. Result depends on archiveType.
5. In my mainMenuViewController, I made transition to the ArchiveTabBarController.
And the problem is: when I'm trying to go to the ArchiveTabBar, it fails with error: unable to deque reuasble cell with identifier "archiveTableViewCell". But when I dropped TabBar, and tried to go to the ArchiveTableViewController, it worked fine.
What is my mistake?
So, I tried next:
in my ArchiveTableViewController, in viewDidLoad(), i put next:
tableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "archiveTableViewCell")
And in cellForRowAt, I used standard text field for stanard cell. Everything works fine. So, the problem is in the cell's registration. Anybody can help with it?
Finally, I did this! I read questions custom UITableCellView programmatically using swift and Custom UITableViewCell register class in Swift, and more questions, and decided to try register XIB file. I created TableViewCell from scratch with XIB, and everything works.

Adding a ViewController to application without a storyboard

How do you implement functions like viewDidLoad() in an empty project without a storyboard?
Sorry for the beginner question. I have been searching for hours now but it still doesn't work.
Thanks for any help!
edit:
I am using a .xib file, just no .storyboard. At the moment I'm setting everything up inside applicationDidFinishLaunching()
edit2:
I think I finally got it:
I created a new project without a storyboard, created a file ViewController.swift together with an ViewController.xib and added
window?.contentViewController = ViewController()
into the applicationDidFinishLaunching function inside the AppDelegate.
I cannot believe I didn't get this sooner.
Sorry BaseZen for your wasted time and thank you very much for trying to help me!
The question is (far) too broad. Please make it more specific, perhaps tailoring it to the part of it that is answered here, just for the sake of others in the future.
There are in-code equivalents to all Storyboard techniques. Your google technique is:
MacOS Swift how to doXyz programmatically
By the way, iOS is probably 10x more popular in terms of search results. So if the platform doesn't matter and you just want to learn how to do it in code, start with iOS.
This will get you started with layout: https://www.hackingwithswift.com/read/6/3/auto-layout-in-code-addconstraints
For example, initializing a non-IBOutlet:
var myButton = NSButton()
Setting its text:
myButton.title = "Hello"
Adding it to a View Controller's view:
view.addSubview(myButton)
Constraining it within the view:
// You have to learn some verbose weird stuff to do this in code
myButton.translatesAutoresizingMaskIntoConstraints = false
view.addConstraint(NSLayoutConstraint( ... ))
Connecting it to a non-IBAction handler, assuming you have a function called func handleAction(sender: AnyObject)
myButton.target = self
myButton.action = #selector(MyViewController.handleAction(_:))
Simplest possible example:
import Cocoa
class ViewController: NSViewController {
var b = NSButton()
override func viewDidLoad() {
super.viewDidLoad()
b.title = "Hi! I am a button"
b.target = self
b.frame = NSRect(x: 100, y: 100, width: 100, height: 25)
b.action = #selector(ViewController.handleButtonPress(_:))
view.addSubview(b)
}
func handleButtonPress(sender: NSButton) {
b.title = "Thank you for clicking."
b.sizeToFit()
}
}

swift dragUP navigation

I'm totally new to coding and was already very happy to get swipe down functionality into my app.
Now I would like to have a swipe up, so if I drag it down I have a UIView that can have some sort of navigation/option screen (all that code is working perfectly) but I want it the other way around (so you can see that it's down there and then you'd swipe it up). I followed a tutorial online that showed me how to get it like this and after I implemented the first code it's already up there and I tried a couple of things but I don't seem to get it.. here's the code:
class ViewController: UIViewController {
var myview:myView!
override func viewDidLoad() {
super.viewDidLoad()
myview = NSBundle.mainBundle().loadNibNamed("customView", owner: self, options: nil).last as signUpView
myview.frame = CGRectMake(0, -self.view.frame.size.height + 66, self.view.frame.size.width, self.view.frame.size.height)
self.view.addSubview(signup)
myview.setup()