I'm adding margins to the TextEditor. While keeping these margins as clickable area.
I was able to add textContainerInset and problem is that added Inset is not clickable.
Current code:
extension NSTextView {
open override var frame: CGRect {
didSet {
textContainerInset = CGSize(width: 72, height: 72)
}
}
}
Current Preview:
Intended behavior (Pages):
Would be grateful for an advice. Thank you very much!
so I found a simple solution and hard one.
1. Simple one
import SwiftUI
extension NSTextView {
open override var frame: CGRect {
didSet {
// Top inset
textContainerInset = NSSize(width: 0, height: 72)
// Left fragment padding <<< This is what I was looking for
textContainer?.lineFragmentPadding = 72
}
}
}
struct TextEditingView: View {
#State private var fullText: String = "One \nTwo \nThree"
var body: some View {
TextEditor(text: $fullText)
.frame(width: 720, height: 480)
.font(.system(size: 24, design: .monospaced))
}
}
As result you get this:
A repository of the demo:
https://github.com/yaosamo/Swift-TextView-Demo
2. Second solution
Using NSParagraphStyle, headIndent, firstLineHeadIndent
I believe this is how indents on Pages on Mac implemented. I do not know tho how they persist default indent. If you open ruler you will see that it set to 1 and you can't go below it.
Using code of
(AppKit) Tab insertion inside of NSTextBlock
class ParagraphStyle {
let bgColor: NSColor
let paragraphStyle: NSParagraphStyle
init(bgColor: NSColor) {
self.bgColor = bgColor
//Set paragraph style
self.paragraphStyle = {
let mutableParagraphStyle = NSMutableParagraphStyle()
let specialBlock = CustomTextBlock(bgColor: bgColor)
mutableParagraphStyle.textBlocks.append(specialBlock)
mutableParagraphStyle.headIndent = 50 // Add indent here
let style = mutableParagraphStyle as NSParagraphStyle
return style
}()
}}
you can add headIndent to text style. And it will work for copy that you insert there. Problem like I said if you start typing Indents break and I don't know how to preserve it.
First one works for me exactly how I want it. Next will figure out how to use headIndent/FirstlineheadIndent
Thanks to this community I was able to find a solution! Don't give up you also can make it! :D
Related
I'm working on trying to recreate the first SwiftUI Tutorial in code without using SwiftUI. In the example, it mentions using a Spacer "to direct the layout to use the full width of the device":
VStack {
Text("Turtle Rock")
HStack {
Text("Joshua Tree National Park")
Spacer()
Text("California")
}
}
For the VStack, HStack, and Text views, I can easily use UIStackView and UILabel. But for the Spacer, I can't seem to find any equivalent in the standard library (no UISpacer or anything like that), which makes me think this is something custom to SwiftUI. In the tutorial, it describes how this Spacer works:
A spacer expands to make its containing view use all of the space of its parent view, instead of having its size defined only by its contents.
So how can I recreate the functionality of this Spacer view programmatically? Do I just add a constraint to the UIStackView to make it full width? Or is there a way to add a subview to the UIStackView that makes it behave like it does in SwiftUI?
I have worked solution using dummy UIView() which has set big constant widthConstraint using low priority - this ensures UIView will grow as much as possible but will not grow over superview constraints since it has lower priority.
Example:
let titleLabel = UILabel()
titleLabel.text = "Joshua Tree National Park"
let spacer = UIView()
// maximum width constraint
let spacerWidthConstraint = spacer.widthAnchor.constraint(equalToConstant: .greatestFiniteMagnitude) // or some very high constant
spacerWidthConstraint.priority = .defaultLow // ensures it will not "overgrow"
spacerWidthConstraint.isActive = true
let descriptionLabel = UILabel()
descriptionLabel.text = "California"
UIStackView(arrangedSubviews: [titleLabel, spacer, descriptionLabel])
Swift 5
You can create an extension for UIView to create a spacer in this way:
extension UIView {
static func spacer(size: CGFloat = 10, for layout: NSLayoutConstraint.Axis = .horizontal) -> UIView {
let spacer = UIView()
if layout == .horizontal {
spacer.widthAnchor.constraint(equalToConstant: size).isActive = true
} else {
spacer.heightAnchor.constraint(equalToConstant: size).isActive = true
}
return spacer
}
}
After that, you can add your spacer into stacks in this way:
// ... some code
let stack = UIStackView(arrangedSubviews: [UIView.spacer(), UILabel(), UIView.spacer(), UILabel(), UIView.spacer()])
stack.axis = .horizontal
stack.distribution = .equalSpacing
// For vertical stacks
let stack = UIStackView(arrangedSubviews: [UIView.spacer(for: .vertical), UILabel(), UIView.spacer(for: .vertical), UILabel(), UIView.spacer(for: .vertical)])
stack.axis = .vertical
stack.distribution = .equalSpacing
I think in this way, you can play with spacer size/layout and stack distribution/alignment to get the desired space.
A plain UIView() can replace a SwiftUI.Spacer as long as distribution is set to fill, since it has no set width constraint. I've only tested this vertically. However, you should note the layout performed by UIStackView and SwiftUI.HStack are not actually the same steps, though the end results may appear similar; something to look into.
I'm using React Native's Image tag to display an image with a variable width and height. I have set the Image to 100x50 and set the resizeMode = contain which works almost perfectly, however if the image is resized, it's coming out completely centered vertically and horizontally. Can this behavior be altered? Mockup screenshot :
The red box indicates the Image element set at 100x50. The laptop pic represents the variable width/height picture. You'll notice that the behavior is pushing everything centered either vertically or horizontally.
In the second example, I would like the picture that takes up 100% of the height and roughly half the width to be pushed to be left aligned. Is this possible?
Thanks!
All - to possibly save some time in the future, this is a quick and dirty method I came up with to resolve this :
logoSize(width, height) {
var maxWidth = 110;
var maxHeight = 30;
if (width >= height) {
var ratio = maxWidth / width;
var h = Math.ceil(ratio * height);
if (h > maxHeight) {
// Too tall, resize
var ratio = maxHeight / height;
var w = Math.ceil(ratio * width);
var ret = {
'width': w,
'height': maxHeight
};
} else {
var ret = {
'width': maxWidth,
'height': h
};
}
} else {
var ratio = maxHeight / height;
var w = Math.ceil(ratio * width);
if (w > maxWidth) {
var ratio = maxWidth / width;
var h = Math.ceil(ratio * height);
var ret = {
'width': maxWidth,
'height': h
};
} else {
var ret = {
'width': w,
'height': maxHeight
};
}
}
return ret;
}
This will take a width and height of an image, run through some ratio calculations, and spit out a set of dimensions based on maxWidth and maxHeight vars at the top. You can then take the dimensions the method above gives you and apply it to an Image element e.g. :
var dims = logoSize(244,127);
<Image style={[styles.logo, {width:dims.width, height:dims.height}]} source={{uri:'/path/to/image.png}} />
Since the calculations rounds, you may need to apply the resizeMode:'contain' to the styles.logo in your Stylesheet.
Hope this saves someone some time.
I've solved a similar task the following way. First you need to set the initial Image styles:
initialImageStyle : { flex:1, resizeMode:'cover' }
This will render the Image in it's original size that we want to capture in a specific onLayout callback. So the trick is to implement that callback in a following manner:
onLayout (e) {
if (this.state.imageSize) { return }
const { x, y,
height,
width } = e.nativeEvent.layout,
sizeX = width - x,
sizeY = height - y,
imageWidth = (sizeX / sizeY) * IMAGE_HEIGHT
this.setState ({ imageSize: { height: IMAGE_HEIGHT, width: imageWidth, resizeMode: 'contain', }})
}
where IMAGE_HEIGHT is a constant height of your image container. Now all you have to do is just substitute the initial image styles using the state:
<Image
style={ this.state.imageSize || styles.initialImageStyle }
source={ /* your image */ }
onLayout={ this.onLayout.bind (this) }
/>
That's it. In your case you may also need to wrap an image in an additional View. Hope this will help.
Good luck!
It is quite possible but don't expect react-native to take care of everything. You may want to write a small algorithm to make this happen. What resizeMode="contain" does is that it checks whether the size of the image is greater/less than the container. If it is greater than the container, it will be resized to fit the container. If it is smaller than the container, it will be rendered as it is.
Regarding the alignment of the image, react-native doesn't really care. Images are placed at the middle of the container. If you want to place the image left-aligned, you'll have to write a small algorithm which compares the actual dimensions of the image and dimensions of the container.
For eg. If the height of the image is greater than the container, set the height of the image equal to the height of the container and calculate the width of the container using "aspect ratio". Read this to get a clear idea of what you'll have to do.
Also, play with position: 'absolute' to better suit your needs. Good luck!
Edit: Sorry for not deriving a ready-made solution for you. You might find the desired algorithm on google though. I just shoved you in the right direction.
I would like to create a smart caption as SoundCloud app did.
See the attachment below,
These two captions:
- •PAN•
- Berlin, Germany
are what I want create.
These captions seem to be executed by sizeToFit or sizeThatFits. However, if using sizeThatFits with background colour (by NSBackgroundColorAttributeName), you won't get padding both before the first and after the last letters and top and bottom paddings as well.
The caption will be organised the exact fit size as these letters.
Anyway, what I would like to do is the exact same caption like the attachment picture.
Cheers,
You have two approaches here. One is to add a bit of padding after calling sizeThatFits. The other is to prepend and append a space to your label title.
The right way about adding the padding, though, would be to extend UILabel and on your subclass, override the method textRectForBounds:limitedToNumberOfLines:. In there, just call the same method on super passing the bounds you receive, only smaller.
I know is an old question but i was looking for the same effect that SoundCloud has on its labels.
here is the subclassing of uilabel
import UIKit
class LabelPine: UILabel {
override func drawTextInRect(rect: CGRect) {
let insets = UIEdgeInsets.init(top: 5, left: 0, bottom: 5, right: 3)
super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
}
override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
super.textRectForBounds(bounds, limitedToNumberOfLines: 0)
return CGRectInset(self.attributedText!.boundingRectWithSize(CGSizeMake(999, 999), options: .UsesLineFragmentOrigin, context: nil), -5, -5)
}
}
And here is the implementation on the respective class:
labelNombre = LabelPine()
labelNombre?.text = nombreUser
labelNombre?.frame = CGRectMake(10, nombrePos.Yo, nombrePos.ancho, nombrePos.alto)
labelNombre?.font = UIFont(name:"Hiragino Sans W3",size: 19)!
labelNombre?.textAlignment = .Left
labelNombre?.backgroundColor = UIColor(white: 1, alpha: 0.5)
labelNombre?.textColor = colorBlentUIColor
labelNombre?.sizeToFit()
header?.addSubview(labelNombre!)
Take notice i call sizeToFit().
I am customizing the title of the Magnific popup/lightbox to include more than one row of content by using the 'change' callback, and modifying the content of
this.content
within the callback. It is working correctly, except for the fact that if the image within the popup is very tall, or the window re-sizes to a smaller height, the calculation that Magnific is doing to adjust the 'max-height' of the image seems to only take into account a single row of text for the title.
Does anyone know what is needed to adjust the max-height calculation of the image to take into account a taller title box?
Thank you
** Edit
A quick hack to jquery.magnific-popup.js around line 461 in the "updateSize:" callback has allowed me to get around this problem. It seems reasonable to for this popup/lightbox to accept a max height in percentage so that it doesn't fill the screen.
Here's my change, I'd appreciate some feedback if possible. Thanks!
updateSize: function(winHeight) {
if(mfp.isIOS) {
// fixes iOS nav bars https://github.com/dimsemenov/Magnific-Popup/issues/2
var zoomLevel = document.documentElement.clientWidth / window.innerWidth;
var height = window.innerHeight * zoomLevel;
mfp.wrap.css('height', height);
mfp.wH = height;
} else {
mfp.wH = winHeight || _window.height();
// ########################################
// CHANGE IS RIGHT HERE TO FORCE 80% height
// ########################################
mfp.wH *= 0.8;
}
// Fixes #84: popup incorrectly positioned with position:relative on body
if(!mfp.fixedContentPos) {
mfp.wrap.css('height', mfp.wH);
}
_mfpTrigger('Resize');
},
You can limit the max height of the image in the resize callback, which will allow more room for the title:
$('a.magnific').magnificPopup({
type: 'image',
callbacks: {
resize: function() {
var img = this.content.find('img');
img.css('max-height', parseFloat(img.css('max-height')) * 0.95);
}
}
});
I'd like to add my contribution. As I wanted to include both titles and descriptions to images. This meant that I couldn't fit all this information in the viewport space. The description was cut off and I was left with a scrollbar.
#alexantd - I tried your callback addition which only works when the window is being resized.
#ajhuddy - Your solution worked perfectly for me. I was able to fit the text in fine. Though the image was considerably small with a lot of space at the top.
I adjusted the padding as to regain 40px space to display a slightly larger image. Here's my CSS to do so. The CSS below allowed me to reduce images to 0.85 (85%).
.mfp-img {
padding: 0px 0px 40px !important;
}
.mfp-close {
margin-top: -40px;
}
else b.wH=a||v.height()**,b.wH*=.9**;b.fixedContentPos
I use Dojo Toolkit 1.7.2 from http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js
I need to show scrollable (with help touch) content inside dialog. Also, if possible, I will need to have transition between views inside dialog like at mobile too.
What I do (simplified version of code):
var dialog = new Dialog();
var view = new ScrollableView({
selected: true
});
//add some content inside view. Content heigh is greater than height of dialog.
If I do this, the dialog tries to fit the whole height of the content.
Next attempt:
var dialog = new Dialog({
style: {
width: 600,
height: 400
}
});
or
dialog.resize({w: 600, h: 400});
Now dialog has fixed height, but inner ScrollableView instance won't scroll to bottom of its content.
When I dig into the source, I find that ScrollableView inherits from dojox/mobile/_ScrollableMixin which inherits from dojox/mobile/scrollable.
The resize() function of dojox/mobile/scrollable uses window height in order to calculate scrolling functionality.
Is there some way to have what I need without implementating my own version of ScrollableView?
Solution:
var dialogRect = domGeometry.getMarginBox(dialog.domNode);
var headerRect = domGeometry.getMarginBox(dialog.titleBar);
var containerNodePaddingTop = domStyle.get(dialog.containerNode, "paddingTop");
var containerNodePaddingBottom = domStyle.get(dialog.containerNode, "paddingBottom");
var viewHeight = dialogRect.h - headerRect.h - containerNodePaddingTop - containerNodePaddingBottom;
var view = new ScrollableView({
selected: true,
height: viewHeight.toString() + "px"
});
// or
// view.set("height", viewHeight.toString() + "px");
Fixed it this way:
var Name = 'yourdialogid';
dojo.query("#"+Name+" .dijitDialogPaneContent").forEach(function(node, index, arr){
dojo.style(node,"overflow","auto");
dojo.style(node,"height",(dojo.position(dijit.byId(Name).domNode).h-80)+"px");
});