I am simply trying to create a view with a contained view that is 50% as tall and high. In the following code, I set constraints to achieve that when I add the view to the super-view. I set translatesAutoResizingMaskToConstraints to false, add the constraints via anchors. I also tried to call setNeedsUpdateConstraints, and add the same constraints in updateConstraints.
But in the following Playground code, I don't see the constrained subview testView. I expect to see an blue view half the size of the orange view, but all i see is orange.
I'm not sure what I am missing here.
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
class LView: UIView {
var testView = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .orange
setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() {
addSubview(testView)
testView.backgroundColor = .blue
testView.text = "....."
//testView.setNeedsUpdateConstraints()
testView.translatesAutoresizingMaskIntoConstraints = false
testView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.5).isActive = true
testView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.5).isActive = true
testView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
testView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
}
let testView = LView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
PlaygroundPage.current.liveView = testView
Related
I’m attaching a UISearchController to the navigationItem.searchController on iOS 13. This works fine: I can use the nice iOS 13-style search bar.
However, I’d like see the large titles and searchBar by default.
I set navigationItem.hidesSearchBarWhenScrolling = false because I want to see the search permanently on my screen, but the search bar replace large titles by default.
Does anyone know how is this is possible?
Check this out
navigationItem.searchController = UISearchController(searchResultsController: nil)
navigationItem.hidesSearchBarWhenScrolling = false
This is how it looks actually
This is how I need to implement(large title and search bar both visible)
For me it worked after adding following lines in the viewDidLoad() method:
searchController.hidesNavigationBarDuringPresentation = true
navigationController?.navigationBar.prefersLargeTitles = true
navigationController!.navigationBar.sizeToFit()
Try this, working fine in my side
private var search = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
search.searchBar.delegate = self
search.searchBar.sizeToFit()
search.obscuresBackgroundDuringPresentation = false
search.hidesNavigationBarDuringPresentation = true
self.definesPresentationContext = true
search.searchBar.placeholder = "Search here"
self.navigationItem.searchController = search
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationItem.hidesSearchBarWhenScrolling = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationItem.hidesSearchBarWhenScrolling = true
}
For large navigation bar use this
For full application navigation bar support please add this extension inside your code.
import UIKit
extension UIViewController {
open func showNavigationBar(_ large: Bool,
_ animated: Bool,
titleColor: UIColor,
barTintColor: UIColor,
fontSize: CGFloat) {
navigationController?.navigationBar.barTintColor = barTintColor
navigationController?.navigationBar.backgroundColor = barTintColor
navigationController?.navigationBar.isTranslucent = true
self.navigationController?.setNavigationBarHidden(false, animated: animated)
if large {
self.navigationController?.navigationBar.prefersLargeTitles = true
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.backgroundColor = barTintColor
appearance.titleTextAttributes = [.foregroundColor: titleColor]
appearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: titleColor,
NSAttributedString.Key.font: UIFont(resource: R.font.robotoMedium, size: fontSize)!]
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.compactAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
} else {
self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: titleColor,
NSAttributedString.Key.font: UIFont(resource: R.font.robotoMedium, size: fontSize)!]
}
} else {
self.navigationController?.navigationBar.prefersLargeTitles = false
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: titleColor,
NSAttributedString.Key.font: UIFont(resource: R.font.robotoMedium, size: 20.0)!]
}
}
}
And Then call this method simply
self.showNavigationBar(true, true, titleColor: UIColor.blue, barTintColor: UIColor.red, fontSize: 32.0)
If then Also not work then use this
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
search.searchBar.becomeFirstResponder()
}
one more solution is that add one UIView with height 0 in storyboard and set-top with safe area and bottom with UIScrollView/UICollectionView/UITableVIew or something else scrollable view and Remove Direct Constraint between TopSafeArea And ScrollableView Top. I know maybe this is not a solution but I did in a storyboard.
I've been trying to achieve the same thing all day long for my app as well and I finally did it.
I wanted to add a searchBar on a UITableViewController and I did it this way.
let searchController: UISearchController = {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.placeholder = "New Search"
searchController.searchBar.searchBarStyle = .minimal
searchController.dimsBackgroundDuringPresentation = false
searchController.definesPresentationContext = true
return searchController
}()
You first create a new UISearchController using a closure, that way you are able to use it globally in your code and customize it easier in the future.
Afterwards in viewDidLoad, you set the searchSontroller.searchResultsUpdater = self and the navigationItem.searchController = searchController.
For me it works perfectly after a lot of trial and error since I'm doing everything programmatically.
This code should work
class NavigationController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
createCustomTabBar()
}
func createCustomTabBar() {
let firstVC = UINavigationController(rootViewController: HomeVC())
firstVC.title = "Home"
firstVC.tabBarItem.image = UIImage(systemName: "house.fill")
viewControllers = [firstVC]
}
class HomeVC: UIViewController {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "Home"
navigationItem.searchController = searchController
}
}
My code uses arkit. All I want to do is when I change the text of the label. The change is reflected in the arkit text. Currently its not. Whatever the arkit label stays and default and never changes no matter what is on the label on the view controller.
import UIKit;import ARKit
class ViewController: UIViewController {
#IBOutlet var arkitView:ARSCNView!
#IBOutlet var outputImageView:UIImageView!
#IBOutlet var ffox: UILabel!
#IBOutlet var enterText: UITextField!
var textGeometry: SCNText!
//4. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//5. Create Our Session
let augmentedRealitySession = ARSession()
override func viewDidAppear(_ animated: Bool) {
arkitView.session.run(configuration)
//1. Create The Text
textGeometry = SCNText(string: ffox.text!, extrusionDepth: 1)
//2. Set It From The UILabel
textGeometry.string = ffox.text!
//3. Create A Material For The Text
let material = SCNMaterial()
material.diffuse.contents = UIColor.green
textGeometry.materials = [material]
//4. Create A Holder Node To Hold The Text
let node = SCNNode()
node.position = SCNVector3(x: 0, y: 0.02, z: -0.01)
node.scale = SCNVector3(x: 0.01, y: 0.01, z: 0.01)
node.geometry = textGeometry
arkitView.scene.rootNode.addChildNode(node)
arkitView.automaticallyUpdatesLighting = true
DispatchQueue.main.async {
self.textGeometry.string = self.ffox.text!
}
}
#IBAction func takeScreenshotAction() {
if let d = enterText.text
{
ffox.text = d
}}}
Looking at your code you may want to set the Font and Font Size e.g:
textGeometry.font = UIFont(name: "Helvatica", size: 3)
Changing The Text:
To change the text of SCNText you need to access it's string property.
Declare you SCNText as a var above your Class Definition e.g.
let textGeometry: SCNText!
And personally I would initialize it in ViewDidLoad.
Then to change it you simply call:
textGeometry.string = "I Have Changed The String"
So in your example assuming as I said that your SCNText is called textGeometry you can do something like this:
textGeometry.string = ffox.text
An Example Of Creating An SCNText Geometry:
class Text: SCNNode{
var textGeometry: SCNText!
/// Creates An SCNText Geometry
///
/// - Parameters:
/// - text: String (The Text To Be Displayed)
/// - depth: Optional CGFloat (Defaults To 1)
/// - font: UIFont
/// - textSize: Optional CGFloat (Defaults To 3)
/// - colour: UIColor
init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 3, colour: UIColor) {
super.init()
//1. Create The Text Geometry With String & Depth Parameters
textGeometry = SCNText(string: text , extrusionDepth: depth)
//2. Set The Font With Our Set Font & Size
textGeometry.font = UIFont(name: font, size: textSize)
//3. Set The Flatness To Zero (This Makes The Text Look Smoother)
textGeometry.flatness = 0
//4. Set The Colour Of The Text
textGeometry.firstMaterial?.diffuse.contents = colour
//5. Set The Text's Material
self.geometry = textGeometry
//6. Scale The Text So We Can Actually See It!
self.scale = SCNVector3(0.01, 0.01 , 0.01)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Using This Class In Your Scene:
//1. Create An SCNText With A White Colour
let nodeToAdd = Text(text: "Hello AR", colour: .white)
//2. Add The Text To The Scene
augmentedRealityView.scene.rootNode.addChildNode(nodeToAdd)
//3. Set The Text's Position Center On The Screen & 1.5m In Front Of The Camera
nodeToAdd.position = SCNVector3(0, 0, -1.5)
Hope this helps...
Update:
The code above works perfectly however since your are having trouble implementing it, here is how you code should look:
class Example: UIViewController {
//1. Create A Reference To Our ARSCNView In Our Storyboard Which Displays The Camera Feed
#IBOutlet weak var augmentedRealityView: ARSCNView!
//2. Create A Label To Display Text
#IBOutlet weak var ffox: UILabel!
//3. Create The Text Gemoetry
var textGeometry: SCNText!
//4. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//5. Create Our Session
let augmentedRealitySession = ARSession()
//--------------------
//MARK: View LifeCycle
//--------------------
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
augmentedRealityView.session.run(configuration)
//1. Create The Text
textGeometry = SCNText(string: ffox.text!, extrusionDepth: 1)
//2. Set It From The UILabel
textGeometry.string = ffox.text!
//3. Create A Material For The Text
let material = SCNMaterial()
material.diffuse.contents = UIColor.green
textGeometry.materials = [material]
//4. Create A Holder Node To Hold The Text
let node = SCNNode()
node.position = SCNVector3(x: 0, y: 0.02, z: -0.01)
node.scale = SCNVector3(x: 0.01, y: 0.01, z: 0.01)
node.geometry = textGeometry
augmentedRealityView.scene.rootNode.addChildNode(node)
augmentedRealityView.automaticallyUpdatesLighting = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I expect the reason your app is crashing is perhaps you have not setup the ffox text in Interface Builder and Wired it up as in the image below:
Also remember the UILabel must contain text!
Final Update:
Since Sam has still not been able to get this to work, I am writing a more in-depth example, which does the following:
(1) The text in a UILabel will be displayed in an SCNTextGeometry on Load.
(2) You can change the string of the SCNTextGeometry by pressing one of three buttons,
(3) You can change the string of the SCNTextGeometry by using a UITextField Input.
All of this has been fully tested and works as does my other example.
You can download the full example here: Sample Project
And of course the `ViewController' Class:
import UIKit
import ARKit
//--------------------
//MARK: TextNode Class
//--------------------
class TextNode: SCNNode{
var textGeometry: SCNText!
/// Creates An SCNText Geometry
///
/// - Parameters:
/// - text: String (The Text To Be Displayed)
/// - depth: Optional CGFloat (Defaults To 1)
/// - font: UIFont
/// - textSize: Optional CGFloat (Defaults To 3)
/// - colour: UIColor
init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 3, colour: UIColor) {
super.init()
//1. Create The Text Geometry With String & Depth Parameters
textGeometry = SCNText(string: text , extrusionDepth: depth)
//2. Set The Font With Our Set Font & Size
textGeometry.font = UIFont(name: font, size: textSize)
//3. Set The Flatness To Zero (This Makes The Text Look Smoother)
textGeometry.flatness = 0
//4. Set The Colour Of The Text
textGeometry.firstMaterial?.diffuse.contents = colour
//5. Set The Text's Material
self.geometry = textGeometry
//6. Scale The Text So We Can Actually See It!
self.scale = SCNVector3(0.01, 0.01 , 0.01)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//--------------------------
//MARK: UITextFieldDelegate
//--------------------------
extension ViewController: UITextFieldDelegate{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print("Current Text = \(textField.text!)")
DispatchQueue.main.async {
self.textNode.textGeometry.string = textField.text!
}
return true
}
}
class ViewController: UIViewController {
//1. Create A Reference To Our ARSCNView In Our Storyboard Which Displays The Camera Feed
#IBOutlet weak var augmentedRealityView: ARSCNView!
//2. Create A Reference To Our SCNNode
var textNode: TextNode!
//2. Create A Label To Display Text
#IBOutlet weak var ffox: UILabel!
//3. Create UITextField So Text Can Be Changed Dynamically
#IBOutlet weak var textChangerField: UITextField!
//3. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//3. Create Our Session
let augmentedRealitySession = ARSession()
//--------------------
//MARK: View LifeCycle
//--------------------
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
augmentedRealityView.session.run(configuration)
//1. Create An SCNText With A White Colour & With The Label's Text
textNode = TextNode(text: ffox.text!, colour: .white)
//2. Add The Text To The Scene
augmentedRealityView.scene.rootNode.addChildNode(textNode)
//3. Set The Text's Position Center On The Screen & 1.5m In Front Of The Camera
textNode.position = SCNVector3(0, 0, -1.5)
//4. Set The Lighting
augmentedRealityView.automaticallyUpdatesLighting = true
//5. Assign The Delegate To The TextChangerField
textChangerField.delegate = self
}
/// Changes The Text In The Button At The Bottom Of The Screen
///
/// - Parameter sender: UIButton
#IBAction func changeText(_ sender: UIButton){
guard let textToDisplay = sender.titleLabel?.text else { return }
DispatchQueue.main.async {
self.textNode.textGeometry.string = textToDisplay
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
In Interface Builder you will have the following:
Im trying to translate this JavaFX class to TornadoFX. Hover im not able to figure out how protected void layoutChildren() should be done with TornadoFX?
This is the code I have so far:
class ReversiSquare(x: Int, y: Int) : View() {
var x by property(x)
fun xProperty() = getProperty(ReversiSquare::y)
var y by property(y)
fun yProperty() = getProperty(ReversiSquare::y)
var highlight: Region by singleAssign()
var highlightTransition: FadeTransition by singleAssign()
val model = ReversiModel
override val root = region {
region {
opacity = 0.0
style = "-fx-border-width: 3; -fx-border-color: dodgerblue"
highlight = this
}
// todo not sure this works with singleAssign
highlightTransition = FadeTransition(Duration.millis(200.0), highlight).apply {
fromValue = 0.0
toValue = 1.0
}
styleProperty().bind(Bindings.`when`(model.legalMove(x, y))
.then("-fx-background-color: derive(dodgerblue, -60%)")
.otherwise("-fx-background-color: burlywood"))
val light = Light.Distant().apply {
azimuth = -135.0
elevation = 30.0
}
effect = Lighting(light)
setPrefSize(200.0,200.0)
this += highlight
addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET) {
if(model.legalMove(x ,y).get()) {
with(highlightTransition) {
rate =1.0
play()
}
}
}
addEventHandler(MouseEvent.MOUSE_EXITED_TARGET) {
with(highlightTransition) {
rate = -1.0
play()
}
}
onDoubleClick {
model.play(x, y)
highlightTransition.rate = -1.0
highlightTransition.play()
}
}
}
I'm not sure what you mean by translating to TornadoFX, but writing the layoutChildren in Kotlin would look something like this:
override fun layoutChildren() {
layoutInArea(highlight, 0.0, 0.0, width, height, baselineOffset, HPos.CENTER, VPos.CENTER);
}
EDIT: You updated the code example to a View, so I think I understand what you want now :)
First, make sure the View doesn't require parameters, because that would make it impossible to inject this view. Either pass parameters using by param() or better yet, inject a ViewModel in the scope of the View, and inject that ViewModel into your View.
Maybe you can add x and y as properties to ReversiModel?
If you need to create a custom Region you can create what would be an anonymous inner class equivalent, in Java speak:
class ReversiSquare : View() {
val model: ReversiModel by inject()
override val root = object : Region() {
// ...
override fun layoutChildren() {
layoutInArea(highlight, 0.0, 0.0, width, height, baselineOffset, HPos.CENTER, VPos.CENTER);
}
}
}
To open this View now, create a new scope and push the ReversiModel into it:
// Create the model, set x, y and other initial state in the model
val model = ReversiModel()
model.x = 42
// Create a new scope and push the ReversiModel into it
val scope = Scope(model)
// Find the ReversiSquare in the new scope
find<ReversiSquare>(scope) {
// Do something with the sequare, like openWindow() or similar
}
im trying to setup a circle image view and when I set the corner radius to perform the operation it does absolutely nothing. I've looked at various threads and solutions none worked
import UIKit
class AlterProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view?.backgroundColor = UIColor.white
navigationItem.title = "Profile Settings"
view.addSubview(selectProfileImage)
///Constraints for all views will go here
_ = selectProfileImage.anchor(view.centerYAnchor, left: view.leftAnchor, bottom: nil, right: nil, topConstant: -275, leftConstant: 135, bottomConstant: 0, rightConstant: 0, widthConstant: 100, heightConstant: 100)
// selectProfileImage.layer.cornerRadius = selectProfileImage.frame.size.width/2
///////////////////////////////////////////////
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//Where all buttons and labels will be added
//will just be a nice looking image view to be next to the profile settings button
lazy var selectProfileImage: UIImageView = {
let selectPicture = UIImageView()
// self.selectProfileImage.layer.cornerRadius = self.selectProfileImage.frame.size.width / 2;
selectPicture.image = UIImage(named: "Paris")
// selectPicture.layer.cornerRadius = selectPicture.frame.size.width / 2;
selectPicture.clipsToBounds = true
selectPicture.translatesAutoresizingMaskIntoConstraints = false
selectPicture.contentMode = .scaleAspectFill
selectPicture.layer.shouldRasterize = true
selectPicture.layer.masksToBounds = true
return selectPicture
}()
///////////////////////////////////////////////////////////////////////////////////
}
None of the methods seem to work im actually kind of stumped right now
Given that you layout with AutoLayout I would suspect the image view simply doesn't have the correct size when you calculate the radius. The image view is initialized with a size of 0,0 and thus the calculated radius will be 0 as well. Instead, move the radius calculation in viewDidLayoutSubviews after calling super:
func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
selectProfileImage.layer.cornerRadius = selectProfileImage.frame.size.width / 2;
selectProfileImage.layer.masksToBounds = true
}
Is there a tvOS API to detect landscape orientation and/or allow use of the Apple TV controller for touch gesture input with the controller held in landscape? (e.g. so the app will get a swipe up event with respect to the floor/gravity whether the controller is portrait or turned sideways).
I've played a little bit around with the AppleTV and the remote. Gesture recognition on the remote is possible. I've copied the sample code from my Blog article about this.
For detecting the orientation of the remote this answer from the Apple Developer forum might be helpful.
import SpriteKit
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed:"Spaceship")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// Add Sprite
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
self.addChild(sprite)
// Register Swipe Events
let swipeRight:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedRight:"))
swipeRight.direction = .Right
view.addGestureRecognizer(swipeRight)
let swipeLeft:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedLeft:"))
swipeLeft.direction = .Left
view.addGestureRecognizer(swipeLeft)
let swipeUp:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedUp:"))
swipeUp.direction = .Up
view.addGestureRecognizer(swipeUp)
let swipeDown:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedDown:"))
swipeDown.direction = .Down
view.addGestureRecognizer(swipeDown)
}
// Handle Swipe Events
func swipedRight(sender:UISwipeGestureRecognizer){
sprite.position = CGPoint(x: sprite.position.x + 10, y: sprite.position.y)
}
func swipedLeft(sender:UISwipeGestureRecognizer){
sprite.position = CGPoint(x: sprite.position.x - 10, y: sprite.position.y)
}
func swipedUp(sender:UISwipeGestureRecognizer){
sprite.position = CGPoint(x: sprite.position.x, y: sprite.position.y+10)
}
func swipedDown(sender:UISwipeGestureRecognizer){
sprite.position = CGPoint(x: sprite.position.x, y: sprite.position.y-10)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}