iOS UIKit Multiline label and UITextView give incorrect visual height width - uiscrollview

Im trying to setup a multiline set of views in a UIScrollView and ultimately a UIStackView. I found out I need to dynamically set the scrollviews height to the content to make it work. So when I went to debug why the values never worked I found that the labelType.frame.size is giving me a massive width or like the text was never wrapped, and the height is the height of the devices view.
If I change around the anchors for the content view I can make the text never line break fueling what I see as the CGSize numbers corresponding to the visual size
While the parent view does not even get a size which is why the default UIScrollView won't even work
content frame (0.0, 0.0)
uitextType (6791.0, 478.0)
labelType (6781.0, 460.5)
In this example I am testing both UITextView and UILabel, I know UITextView is not recommended but testing anyway, it has the same behavior with or without
Worse case I guess I can mix SwiftUI into the Uiview and carry on with work.
override func viewDidLoad() {
super.viewDidLoad()
}
override func loadView() {
super.loadView()
let content = UIView()
content.backgroundColor = UIColor.green
content.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(content)
let constraints2 = [
content.topAnchor.constraint(equalTo: self.view.topAnchor),
content.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
content.heightAnchor.constraint(equalTo: self.view.heightAnchor, constant: 0),
content.widthAnchor.constraint(equalTo: self.view.widthAnchor),
// content.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
// content.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
]
NSLayoutConstraint.activate(constraints2)
let uitextType = UITextView()
uitextType.backgroundColor = .clear
uitextType.text = lotatext
uitextType.isScrollEnabled = false
uitextType.translatesAutoresizingMaskIntoConstraints = false
content.addSubview(uitextType)
uitextType.topAnchor.constraint(equalTo: content.topAnchor).isActive = true
uitextType.leadingAnchor.constraint(equalTo: content.leadingAnchor).isActive = true
uitextType.heightAnchor.constraint(equalTo: content.heightAnchor).isActive = true
uitextType.widthAnchor.constraint(equalTo: content.widthAnchor).isActive = true // -40 2x?! lame
uitextType.font = UIFont.preferredFont(forTextStyle: .body)
uitextType.contentInsetAdjustmentBehavior = .automatic
uitextType.sizeToFit()
uitextType.contentMode = .topLeft
let labelType = UILabel()
labelType.backgroundColor = .clear
labelType.text = lotatext
labelType.numberOfLines = 0
labelType.lineBreakMode = .byWordWrapping
labelType.translatesAutoresizingMaskIntoConstraints = false
content.addSubview(labelType)
labelType.topAnchor.constraint(equalTo: content.topAnchor).isActive = true
labelType.leadingAnchor.constraint(equalTo: content.leadingAnchor).isActive = true
labelType.heightAnchor.constraint(equalTo: content.heightAnchor).isActive = true
labelType.widthAnchor.constraint(equalTo: content.widthAnchor).isActive = true
labelType.font = UIFont.preferredFont(forTextStyle: .body)
labelType.sizeToFit()
print("content frame \(content.frame.size)")
print("uitextType \(uitextType.frame.size)")
print("labelType \(labelType.frame.size)")
}
// testing text
let lotatext = """
Every day is taco ipsum tuesday. It’s a wonderful morning for breakfast tacos. 50 cent tacos! I’ll take 30. CARNE ASADA!! BARBACOA!! I’d have to say, those tacos are on fleek. It’s long been rumored that the chupacabra is really just a crazed man who’s local taco shop went out of business. Does guac cost extra? BARBACOA!! TACOS!! It’s raining tacos, from out of the sky, tacos, don’t even ask why. CARNE ASADA!!
50 cent tacos! I’ll take 30. I’d have to say, those tacos are on fleek. Let’s do a beef and a chicken, and one with both. I’ve been following that taco truck around all day. Make it a double there pal. It’s long been rumored that the chupacabra is really just a crazed man who’s local taco shop went out of business. I think I’ve overdosed on tacos. Tacos Al pastor/De Adobada are made of thin pork steaks seasoned with adobo seasoning, then skewered and overlapped on one another on a vertical rotisserie cooked and flame-broiled as it spins.
... snipped extras paragraphs
"""

Related

Updating rotary knob in real time with SwiftUI

I'm currently trying to convert a slider in to a rotary knob and having a tough time of it all. The knob works in design but i'm struggling to set the correct value within the knob and as a result change the value within the app in real time.
I'm using AVAudio to set up an engine for people to record with that has effects like Reverb and Delay.
The reverb value is set as followed within the Audio Class:
#Published var reverbValue: Float = 0.0
and later on referenced in a function to change it's value
func changeReverbValue() {
setReverb.wetDryMix = reverbValue
}
When I use a regular slider as follows the change works:
Slider(value: $recordingsettings.reverbValue, in: Float(0.0)...recordingsettings.reverbMaxValue, onEditingChanged: { _ in
self.recordingsettings.changeReverbValue()
}).accentColor(Color.white)
As mentioned the knob works fine in its design:
ZStack {
Knobs(color: .orange)
.rotationEffect(
.degrees(max(0, initialCircleState()))
)
.gesture(DragGesture(minimumDistance: 0)
.onEnded({ _ in
startDragValue = -1.0
})
.onChanged { dragValue in
let touchDifferential = touchDifference(dragValue)
setInitialDragVal()
let computedTouch = computeTouch(touchDifferential)
print(computedTouch)
baseValue = getBaseVal(computedTouch)
let normalizeVal = baseValue / touchAmt
value = Float(normalizeVal * rngOffset(range: bounds) + bounds.lowerBound)
print("vaule is: \(value)")
}
)
GrayCircle(bounds: bounds)
OrangeCircle(baseValue: $value, bounds: bounds)
}
.rotationEffect(bounds.lowerBound < 0 ? .degrees(90) : .degrees(107))
I've had some success connecting the knob to the reverb value to the point where the slider also moves when the rotary knob does, however the changeReverbValue function doesn't work.
The success comes from setting the value within the knob view as follows:
#Binding var value: AUValue
And then referencing the knob on the same struct of the main view as the slider:
Knob(value: $recordingsettings.reverbValue, bounds: 0...CGFloat(recordingsettings.reverbMaxValue))
.onTapGesture {
self.recordingsettings.changeReverbValue()
}
The on tap gesture was a way in which I thought it might call the change reverb value function when the knob was turned but to no avail.
The binding value passed in the knob also has other challenges. For some reason when I playback audio without headphones and then turn the knob the audio starts to stutter. This doesn't happen with headphones and I find that pretty weird.
Anyone know how I could reference the reverb value within the rotary knob and have the changeReverbValue function called at the same time?
I just want to replace the slider with something that looks better. Otherwise i'm going to have to leave this for a bit and just implement the sliders instead throughout the app.
If I don't set the value of the knob as #binding in the rotary knob view the track doesn't stutter on playback but then I don't know if it's possible to change the reverb value without a #binding var.
I struggled to parse a precise singular problem statement from the narrative, so this is perhaps just an off-base commentary and not a solution. I walked away thinking your problem is: a custom UI component is "jumpy/stuttery" during interaction and produces similarly punctate effects on app state.
If that's fair, I worked around the same issue in my first SwiftUI app. The cause could be two things:
Not using the right async queue by accident.
Forcing an #State or #Published property to update for all global state changes. That means you are pushing stale state from earlier back into an interaction, possibly with a circular feedback loop.
The solution is pretty simple. Request and consume model updates with both a value and a source tag. Throttle and filter out the self-tag to keep local state responsive to only one just-in-time data stream.
I used that pattern in that first app (free, Inclusivity for Mac) to coordinate an HSV color wheel and color channel custom slider components. The wheel, sliders, and other interactions feed/read a shared Combine pipeline (CurrentValueSubject<SourcedColorVector,Never>.erasedToAny()).
Some sample gestures, which simply punt the gating work to a view model:
The HSVWheel drag-around or click gesture
private func touchUpInWheel() -> ExclusiveGesture<_ChangedGesture<DragGesture>, _EndedGesture<DragGesture>> {
ExclusiveGesture(
DragGesture(minimumDistance: 10, coordinateSpace: .named(wheel))
.onChanged { change in
let adjusted = CGPoint(x: change.translation.width - targetDiameter + change.startLocation.x / 2,
y: change.translation.height - targetDiameter + change.startLocation.y / 2)
vm.setHueSat(drag: adjusted)
},
DragGesture(minimumDistance: 0, coordinateSpace: .named(wheel))
.onEnded { end in
let click = CGPoint(x: end.location.x - vm.radius,
y: end.location.y - vm.radius)
vm.setHueSat(click: click)
}
)
}
A typical slider gesture (this is the vertical value slider)
private func tapAndDrag() -> _EndedGesture<_ChangedGesture<DragGesture>> {
DragGesture(minimumDistance: 0,
coordinateSpace: .named(valuePickerSpace))
.onChanged { value in
let location = value.location.y - .valueSliderGestureOffset
vm.setValueKnobLocation(raw: location)
}
.onEnded { end in
let location = end.location.y - .valueSliderGestureOffset
vm.setValueKnobLocation(raw: location)
}
}

How to make `NSPopUpButton` with `NSControl.ControlSize.mini` programmatically?

I tried setting controlSize, and it didn't work.
I tried setting NSMenuItem.attributedTitle, and it didn't work.
Setting NSMenuItem.attributedTitle made text smaller, but didn't make checkmarks smaller. Therefore result looks broken.
Here's code to reproduce.
let testFont1 = NSFont.menuFont(ofSize: NSFont.systemFontSize(for: .mini))
let menu1 = NSMenu()
menu1.font = testFont1
menu1.addItem(withTitle: "AAA", action: nil, keyEquivalent: "")
let item2 = NSMenuItem()
item2.attributedTitle = NSAttributedString(string: "BBB", attributes: [.font: testFont1])
menu1.addItem(item2)
let popup1 = NSPopUpButton()
popup1.menu = menu1
popup1.controlSize = .mini
popup1.sizeToFit()
window.contentView?.addSubview(popup1)
How to make a mini-sized pop-up button properly with no NIB and only code?
Just assign mini-size font to the NSPopUpButton instance.
popup1.font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .mini))
Nothing else is really required. The code can be shorten like this.
let popup1 = NSPopUpButton()
popup1.controlSize = .mini
popup1.font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .mini))
popup1.addItem(withTitle: "AAA")
popup1.addItem(withTitle: "BBB")
popup1.sizeToFit()
window.contentView?.addSubview(popup1)
This applies equally to any other control-based classes. You need to set both controlSize and font. It seems controlSize controls only graphical appearance part and font controls text renderings.

CocosSharp game on Android startup takes a long time to draw graphics

We have a somewhat simple game written using CocosSharp. The game is within another Xamarin.Forms app. When the player clicks to play the game, we bring them to a splash screen. On iOS this screen displays immediately but on Android, the screen is just black for about 15 seconds. The music plays pretty much immediately on both platforms.
The following is called from the ViewCreated event of the CocosSharpView.
InitializeAudio();
var scene = new Scenes.SplashScene(GameView);
GameView.RunWithScene(scene);
The hang up seems to be when creating labels. The following take ~10 seconds to complete with 99% of it being in the constructor of the first label. We call our CreateText in the constructor.
private void CreateText()
{
var label = new CCLabel("Text 1", "BD_CARTOON_SHOUT.TTF", 80)
{
PositionX = layer.ContentSize.Width / 2.0f,
PositionY = layer.ContentSize.Height / 1.5f,
Color = CCColor3B.DarkGray
};
layer.AddChild(label);
label = new CCLabel("Text 2", "BD_CARTOON_SHOUT.TTF", 60)
{
PositionX = layer.ContentSize.Width / 2.0f,
PositionY = 50f,
Color = CCColor3B.DarkGray
};
layer.AddChild(label);
}
Keep in mind this only happens on Android.
Thanks in advance!
I had the same issue several weeks ago. The problem was CCLabel constructor
where it didn't know which type of font are you using and therefore trying all other types before finally trying the last one which is correct. You need to specify font type in CCLabel constructor like this:
var label = new CCLabel("Text 1", "BD_CARTOON_SHOUT.TTF", 80, CCLabelFormat.SystemFont);
Hope it helps.

UIScrollView - Duplicate code produces different result

I have a small test project that I'm trying to import into my production project. The ViewController that is causing the problem consists of a back ground image and a Scroll View (SV).
3 images appear in the test SV, yet only 2 appear in production. There appears to be a gap where the first image should appear.
Please note. The first image is the background image for the VC. I set it, then delete it from the array that feeds the scrollview.
Here are the two ViewControllers. Please note I embedded the VC in a TabBar and NavBar controller in test because that is what I have int production.
What is most puzzling is the code is exactly the same. The image URL's are the same. But the number of UIImageViews added to the scrollView are different. Note the last print statement in the code:
func setupList() {
print(foodPairings.count)
let imageStringURL = foodPairings[0].imageURL
let encodedURL = imageStringURL.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)
guard let imageURL: URL = URL(string: encodedURL!) else {return}
bgImage.af_setImage(withURL: imageURL)
foodPairings.removeFirst()
print(foodPairings.count)
print(foodPairings.indices)
for i in foodPairings.indices {
let imageView = UIImageView()
let imageStringURL = foodPairings[i].imageURL
let encodedURL = imageStringURL.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)
guard let postURL: URL = URL(string: encodedURL!) else {return}
imageView.af_setImage(withURL: postURL, placeholderImage: #imageLiteral(resourceName: "Placeholder"), filter: nil, progress: nil, imageTransition: .noTransition, runImageTransitionIfCached: false, completion: nil)
imageView.tag = i
imageView.contentMode = .scaleAspectFill
imageView.isUserInteractionEnabled = true
imageView.layer.cornerRadius = 20.0
imageView.layer.masksToBounds = true
listView.addSubview(imageView)
//attach tap detector
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapImageView)))
}
print(listView.subviews.count)
listView.backgroundColor = UIColor.clear
positionListItems()
}
The print statements in Test result in:
4 3
0..<3 4
Production prints the following:
4 3
0..<3 5
Why is listView.subviews.count different in production?
I have resolved this, but it is not clear what the source of the issue was.
In Test, my Scroll View had one subview even prior to me adding the array of UIImageViews. In Production, the Scroll View had two subviews prior to me adding the images in the array.
Just prior to the loop where I add my subviews, I remove all subviews from the ScrollView:
listView.subviews.forEach({ $0.removeFromSuperview() })
for i in foodPairings.indices {
print("SubviewCount: \(listView.subviews.count)")
let imageView = UIImageView()
...
Both my Test and Production macs are running XCode 9 Swift 4. Still remains a mystery why subview count was different.

line spacing in dynamically created swift 3/xcode labels

I'm having an issue where I am getting a list of skills back from an api and I want them to stack one on top of the other in two different sections, a left column and a right column. It works well but if the skill is longer than the width of the label it drops to a new line with the same spacing as the rest of the labels. The skill Adobe Creative Suite looks like Adobe Creative as one and Suite as another. I would like Suite to be underneath Adobe Creative but much closer so you can tell it's all one skill.
My code is here:
lblLeft.text = ""
lblRight.text = ""
if let expertiseCount = helper.expertise {
for i in 0..<expertiseCount.count {
if i % 2 == 0 {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 10
let attrString = NSMutableAttributedString(string: lblLeft.text! + "\(expertiseCount[i].name ?? "")\n")
attrString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle, range: NSMakeRange(0, attrString.length))
lblLeft.attributedText = attrString
} else {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 10
let attrString = NSMutableAttributedString(string: lblRight.text! + "\(expertiseCount[i].name ?? "")\n")
attrString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle, range: NSMakeRange(0, attrString.length))
lblRight.attributedText = attrString
}
}
}
I've already tried line spacing and that just changes the size between all lines so the space between Adobe Creative and Suite takes on that change as well.
Try:
lblLeft.numberOfLines = 0
lblLeft.lineBreakMode = .byWordWrapping
lblLeft.sizeToFit()
By setting number of lines to zero and turning word wrapping on, the label will grow to the required number of lines. sizeToFit() should size it properly.

Resources